- OnMovementUpdated


- 등반의 준비가 어느 정도되면 SetMovementMode를 통해 PhyCustom을 호출 할 수 있다. 이때 앞서 설명했듯이 여러종류의 PhysCustom이 존재할 경우 호출할 수 있도록 하기 위해 두번째 매개변수로 Custom Enum을 받는다.
3. Input
등반을 시작하고 중지하기 위해 TryClimbing 및 CancelClimbing의 두 함수와 함께 플레이어의 의도를 저장하는 부울 bWantsToClimb를 만든다. bWantsToClimb이 true인지 확인하여 OnMovementUpdated의 코드를 대체할 수도 있다.


*Input Mapping Context에 설정되어 있는 Climbing과 CancelClimbing. 각 입력에서 호출되는 함수에서는 Climbing을 하고자 하는 상태값인 bool 값을 변경해준다.
Climbing으로의 모드 전환후 등반이 시작되면 움직일 수 있는데 이때 기존의 MoveForward와 MoveRight와는 방향과 방향을 구하는 방법이 달라진다.
등반 시에는 등반을 하는 벽을 기준으로 상대적인 Forward와 Right를 구해야한다.
이에 가장먼저 Climbing상태를 확인가능한 함수를 만든다. 상태에 따라 방향값을 구하는 방식을 달리해야하기 때문.

* 현재 적용중인 MovementMode를 확인하여 Climbing을 판별한다

- ForwardDirection = FVector::CrossProduct(MovementComponent->GetClimbSurfaceNormal(), -(OwnerMainCharacter->GetActorRightVector()));
벽 표면의 Normal과 케릭터의 왼쪽방향 벡터를 외적하여 Forward 벡터를 구한다. Camera 좌표계의 세 축을 구하는 원리와 같다고 보면 된다. 두 벡터의 외적의 결과는 두 벡터에 수직하는 벡터를 구할 수 있는데 있는데 이를 이용한 것.
- RightDirection = FVector::CrossProduct(MovementComponent->GetClimbSurfaceNormal(), OwnerMainCharacter->GetActorUpVector());
Forward 벡터와 원리는 같고 이용하는 벡터만 달라졌다. 이때, ActorUpVector와 표면 벡터의 외적값에 해당하는 수직벡터가 케릭터의 오른쪽 축 벡터가 될 수 있는지는 경우에 따라 달라 질 수 있다.
4. Climbing Physics
- OnMovementModeChanged
위에서 언급이 있었던대로 SetMovementMode가 호출되면 OnMovementModeChanged가 트리거 되므로 모드가 변경 되었을 때 필요한 설정들에 대한 부분을 여기서 세팅할 수 있기에 OnMovementModeChanged를 override하여 Climbing에 필요한 설정들을 세팅하여 준다.
bUseControllerDesiredRotation, bUseControllerRotationYaw 등을 통해 케릭터를 컨트롤하고 있었다면 케릭터가 키보드나 마우스의 입력에 따라 회전을 한다. 등반 중에는 해당 동작이 필요하지 않으므로 해당 기능을 false로 처리한다. 또한 등반을 하게되면 걷을때보다 더 작은 Capsule Component의 크기를 요구한다. 따라서 Capsule의 크기도 줄여준다.

- if (IsClimbing())
{
bUseControllerDesiredRotation = false;
OwnerMainCharacter->bUseControllerRotationYaw = false;
UCapsuleComponent* Capsule = CharacterOwner->GetCapsuleComponent();
Capsule->SetCapsuleHalfHeight(Capsule->GetUnscaledCapsuleHalfHeight() - ClimbingCollisionShrinkAmount);
}
등반이 가능한지 검사 후 가능하다면 bUseControllerDesiredRotation 와 bUseControllerRotationYaw를 false로 하여 케릭터의 회전을 제한하고 미리 생성해 두었던 멤버변수 ClimbingCollisionShrinkAmount의 값을 통해 해당변수의 크기만큼 CapsuleComponent의 크기를 줄여준다.
- const bool bWasClimbing = PreviousMovementMode == MOVE_Custom && PreviousCustomMode == ECMOVE_Climbing;
bWasClimbing은 이동모드의 변경이 요청됐을 시 이전에 동작하던 모드가 Climbing인지를 확인하는 변수,
나머지는 위에서 Climbing시에 설정했던 값을 정확히 반대로 되돌리고 있다.

- OnMovementModeChanged함수를 구성했다면 PhysClimbing을 정의해야하는데 여기에 사용될 함수들.

- PhyClimbing의 구성 및 앞서 선언된 함수들의 사용 형태

- bIsOnCeiling은 지금 오르고자 하는 벽의 노말을 계산하여 천장인지 확인하는 것이도 bWantsToClimb은 입력으로 등반을 취소하는 경우이다. CurrentClimbingNormal.IsZero()는 타려고 하는 벽이 없어지는 경우이므로 셋 중 하나라도 True일 경우 등반을 중지하도록 true값을 반환한다.

- BOTW의 Climbing을 구현하므로 등반할 때의 느낌을 비슷하게 조절하기위해 사용되는 수치를 다른 모드와는 달리한다. 빠르게 움직이거나 갑자기 방향을 빠르게 전환하는 등의 움직임을 제한하기위해 가속과 감속에 대한 부분을 제어한다.
이를 위해 멤버변수 MaxClimbingSpeed, MaxClimbingAcceleration, BrakingDecelerationClimbing을 사용하고 마찰을 0으로 하여 움직임에 부드러움을 준다.

- Climbing의 상태일 때에만 지정된 변수값을 리턴하게 하여 수치를 조절하고 있다.

- ComputeClimbingVelocity함수에서는 속도는 계산했지만 실제로 이동이 일어나지는 않았다. MoveAlongClimbingSurface는 실제로 속도에 따라서 케릭터를 표면의 어느 위치로 이동시킨다. ComputeClimbingVelocity에서 계산된 속도에 deltaTime을 곱해주어서 시간에따른 이동량을 계산하고 SafeMoveUpdatedComponent를 통해 이동시킨다.

- 클라이밍 동작에 대한 회전은 캐릭터가 벽의 법선과 반대 방향을 바라보도록 만들 수 있다. 이를 코드로 구현하기 위해, -CurrentClimbingNormal과 같은 X 축(앞쪽 방향)을 가진 회전 행렬을 생성할 수 있다. 그러나 즉각적인 회전 변경은 부드럽지 않을 수 있다. 대신, 현재 회전에서 목표 회전까지 보간을 진행하여 ClimbingRotationSpeed가 속도를 제어하도록 한다.

- const FVector Forward = UpdatedComponent->GetForwardVector();
const FVector Location = UpdatedComponent->GetComponentLocation();
const FQuat Rotation = UpdatedComponent->GetComponentQuat();
케릭터의 현재를 기준으로 Forward Vector, Location, Rotation을 구한다.
- const FVector ForwardDifference = (CurrentClimbingPosition - Location).ProjectOnTo(Forward);
const FVector Offset = -CurrentClimbingNormal * (ForwardDifference.Length() - DistanceFromSurface);

CurrentClimbingPosition은 벽 표면에서의 Climbing Position이고 Location은 케릭터의 실제 위치이다. ForwardDifference 은 이 두 점을 이용해 Location에서 Climbing Position을 향하는 벡터를 구하고 그 벡터를 Forward Vector에 투영시킨 값이다. 위의 그림에서 노란색이 Climbing Position과 Location을 이용해 구한 벡터이고 빨간색이 Forward Vector로 노란벡터 P를 빨간벡터 X로 사영한다. 해당 계산을 하는 이유는 Forward Vector로의 사영까지를 진행하는 경우 일관된 거리를 구할 수 있기때문이라한다.
ForwardDifference의 길이는 케릭터와 벽면 사이의 길이가 되는데 여기서 멤버변수로 선언된 DistanceFromSurface의 길이만큼을 빼주고 CurrentClimbingNormal을 곱해주어 케릭터에서 ClimbingPosition을 바라보는 벡터를 구하게 된다. 이때, DistanceFromSurface의 길이만큼 케릭터는 벽과 떨어지게 되는데 간격을 조절하길 원하면 해당 값을 조절하면 된다.
- UpdatedComponent->MoveComponent(Offset * SnapSpeed * deltaTime, Rotation, bSweep);
케릭터를 앞서 구한 Offset만큼 이동시켜 울퉁불퉁한 표면의 벽을 올라타더라도 일정한 거리만큼 벽과 떨어져 있을 수 있도록 한다. Rotation은 애니메이션을 추가해보면 알겠지만 이동과 케릭터가 바라보는 방향이 다르다. 따라서 이동을 위해서는 별도로 구한 방향 벡터를 적용해야하겠지만 케릭터가 바라보고 있는 방향은 그와 별개로 변함이 없다. 따라서 앞에서 구한 Rotation값을 그대로 적용한다.

- 기본적인 원리는 CurrentClimbingPosition과 CurrentClimbingNormal을 SweepAndStoreWallHits에서 구한 CurrentWallHits값들의 총합을 갯수로 나누는 평균값으로 계산을 해내는 것이다. 다만 그 과정에서 수행한 SweepMultiByChannel이 케릭터의 Location과 Forward 벡터를 통해 Start와 End를 지정하여 정확도가 떨어진다는 단점이 존재한다.

* 원래 하늘 색의 두 구체는 모두 정면을 바라보는 같은 노말값을 가져야하지만 케릭터를 향하는 노말값을 출력한다.(SweepMultiByChannel을 통해 구한 값의 문제점)
- const FVector Start = UpdatedComponent->GetComponentLocation();
const FCollisionShape CollisionSphere = FCollisionShape::MakeSphere(6);
for (const FHitResult& WallHit : CurrentWallHits)
{
const FVector End = Start + (WallHit.ImpactPoint - Start).GetSafeNormal() * 120;
FHitResult AssistHit;
GetWorld()->SweepSingleByChannel(AssistHit, Start, End, FQuat::Identity, ECC_WorldStatic, CollisionSphere, ClimbQueryParams);
CurrentClimbingPosition += AssistHit.ImpactPoint;
CurrentClimbingNormal += AssistHit.Normal;
}
해당 코드의 과정은 위의 그림에서 나타난 문제점을 해결하기 위하여 케릭터의 Location을 Start로 잡고 End를 케릭터 Location에서 ImpactPoint를 향하는 향하도록하여 재검사를 실시해 정확도를 높인 값을 구한 후 CurrentClimbingPosition과 CurrentClimbingNormal에 평균값을 구하기위해 값을 축적하는 과정이다.

* 해당 방식으로 검사를 진행할 경우 해결된 충돌지점의 Normal값 문제
'Unreal Engine' 카테고리의 다른 글
Outline Shader - 2 (0) | 2023.09.29 |
---|---|
Outline Shader - 1 (1) | 2023.09.29 |
GAS(Gameplay Ability System) Documentation (0) | 2023.09.28 |
BOTW Climbing시스템(젤다의 전설 벽타기) -3 (0) | 2023.09.28 |
BOTW Climbing시스템(젤다의 전설 벽타기) -1 (0) | 2023.09.28 |