5. Animation
애니메이션은 참고한 사이트에서 추천하는 Climbing Animation Set을 사용했다.
Climbing으로의 전환은 기본적으로 Input을 통해서만 가능하고 Enum값을 통해 애니메이션을 조정했다. Climbing으로의 전환 전에 거치는 과정을 통하여 전환 애니메이션을 실행하고 Climbing Idle로 가도록 하였다.
BlendSpace의 기본 구성은 아래와 같다.
* Asset에 방향별 애니메이션이 잘 구분되어 있어 따로 작업하지 않고 집어 넣기만 하였다. 각 축의 최소값과 최대값이 -120 ~ 120인것은 멤버변수로 MaxClimbingSpeed가 120으로 설정되어 있기때문.
케릭터가 Climbing을 하기 위해서는 BS에 Up/Down 과 Left/Right에 넣어줄 값을 가져와야하는데 이 값은 Characater->GetVelocity()가 적절하지만 이때 얻을 수 있는 Velocity에는 Climbing을 고려하지 않은 Character의 Rotation값이 적용되어 있으므로 Character의 Rotation을 역으로 곱해주어 제거한 후 사용해야한다.
* UnRotatedVelocity를 구하여 ABP에 전달한다.
6. Climbing Down
등반에서 내려올 때 바닥에서 가까운 경우를 판별하기 위해서는 발바닥쪽에서 바닥을 향해 Line Trace를 진행하여 바닥과의 충돌여부를 판별해야한다. false인 경우에는 여전히 등반이 가능하지만 true를 반환하는 경우에는 바닥과 가깝거나 이미 바닥에 올라가있는 경우이므로 내려와야한다.
* 바닥을 검사하는 함수로 Start를 케릭터 Location, End를 DownVector * FloorCheckDistance(100)으로 되어 있다.
- const bool bOnWalkableFloor = FloorHit.Normal.Z > GetWalkableFloorZ();
걸을 수 있는 바닥인지를 판별한다. 바닥의 Normal값이 GetWalkableFloorZ보다 크거나 같다면 걸을 수 있는 바닥이다.
- const float DownSpeed = FVector::DotProduct(Velocity, -FloorHit.Normal);
바닥을 향해 떨어지는 속도를 구하는 계산식. 어떤 속도를 나타내는 벡터와 방향을 가리키는 Normal값과 내적을 진행할 경우 Normal값이 가리키는 방향의 속도를 결과값으로 반환한다.
- const bool bIsMovingTowardsFloor = DownSpeed >= MaxClimbingSpeed / 3 && bOnWalkableFloor;
임의의 최소속도 값을 통해서 바닥으로 내려오는 속도의 값을 판별하고, 걸을 수 있는 바닥인지의 여부와 결합하여 내려오고 있는지를 확인한다.
- const bool bIsClimbingFloor = CurrentClimbingNormal.Z > GetWalkableFloorZ();
앞의 세 연산식은 바닥을 향해 오고 있는 경우이고 CurrentClimbingNormal.Z > GetWalkableFloorZ();의 연산은 지금 바닥 위에 서있는지에 대한 검사.
- return bIsMovingTowardsFloor || (bIsClimbingFloor && bOnWalkableFloor);
내려오고 있는지에 대한 검사는 이미 충분하지만 바닥 위에 서잇는지에 대한 검사는 경우에 따라서 CurrentClimbingNormal의 판별만으로 부족하다. 따라서 bOnWalkableFloor의 결과값 역시 바닥을 찾았는지를 추가하여 검사의 정확도를 올려준다
7. Climb Up Ledges
Climb Up Ledges의 애니메이션을 동작시킬 때 RootMotion이 아닌 경우 케릭터의 이동을 계산하여 자연스러운 움직임에 맞게 이동시키면서 애니메이션을 실행하여야하는데 이는 수행하기 매우 어렵다.
따라서 RootMotion으로 자연스러운 움직임을 출력할 수 있도록한다.
RootMotion 출력을 위해 Animation Montage를 만들어서 오를 수 있는 Legde에 도달하면 실행시켜준다.
- const float UpSpeed = FVector::DotProduct(Velocity, UpdatedComponent->GetUpVector());
const bool bIsMovingUp = UpSpeed >= MaxClimbingSpeed / 3;
속도와 UpVector의 내적으로 UpVector 성분의 크기를 구해서 속도를 기준으로 올라가고 있는지를 판별
- if (bIsMovingUp && HasReachedEdge() && CanMoveToLedgeClimbLocation())
케릭터가 Climb Up ledge를 수행하기 위해서는 전제조건으로 세가지가 충족되어야한다.
1- 올라가고 있는 중이어야 한다.(Climbing 상태 && UpMoving)
2- Ledge(벽돌 등으로 이루어진 돌출부)에 있어야 한다.
3- 올라간 후에 캐릭터가 서 있을 수 있어야 한다. 즉, 케릭터가 서있을 수 있는 공간이 있어야한다.
해당 if문은 위 세가지 조건을 모두 충족하는지를 검사하는 것.
- const FRotator StandRotation = FRotator(0, UpdatedComponent->GetComponentRotation().Yaw, 0);
UpdatedComponent->SetRelativeRotation(StandRotation);
AnimInstance->Montage_Play(LedgeClimbMontage);
OnLedgeUpClimbDelegate.ExecuteIfBound();
위에서 Ledge를 등반할 수 있다는 걸 판별했기때문에 회전값을 세팅하고 Ledge Up을 하는 AnimMontage를 실행한다.
- HadReachedEdge
- const UCapsuleComponent* Capsule = CharacterOwner->GetCapsuleComponent();
const float TraceDistance = Capsule->GetUnscaledCapsuleRadius() * 2.5f;
return !EyeHeightTrace(TraceDistance);
눈 위치에서 Line Trace를 진행하여 충돌이 없을 경우에 True를 반환하는데 Character CapusleComponent 반지름의 2.5배에 달하는 거리만큼 검사하여 올라설때 충분한 공간이 있을 경우만 올라설 수 있도록 한다.
- EyeHeightTrace를 진행할 때 현재 Climbing 중인 상황이라면 앞에서 CapsuleComponent의 크기를 줄였던 상황이라 줄였던 크기만큼 다시 키워준 후 검사를 진행해야한다. 따라서 EyeHeightTrace에 해당 코드를 삽입한다.
- CanMoveToLedgeClimbLocation
- const FVector VerticalOffset = FVector::UpVector * 160.f;
const FVector HorizontalOffset = UpdatedComponent->GetForwardVector() * 120.f;
const FVector CheckLocation = UpdatedComponent->GetComponentLocation() + HorizontalOffset + VerticalOffset;
케릭터를 기준으로 위로 160만큼 앞으로 120만큼의 거리까지 검사를 진행하기 위해 좌표를 세팅한다.
- FHitResult CapsuleHit;
const FVector CapsuleStartCheck = CheckLocation - HorizontalOffset;
const UCapsuleComponent* Capsule = CharacterOwner->GetCapsuleComponent();
const bool bBlocked = GetWorld()->SweepSingleByChannel(CapsuleHit, CapsuleStartCheck, CheckLocation, FQuat::Identity, ECC_WorldStatic, Capsule->GetCollisionShape(), ClimbQueryParams);
return !bBlocked;
SweepSingleByChannel을 진행할때 케릭터의 CapsuleComponent 모양대로 검사를 진행하기위해 CapsuleComponent를 가져왔고, 케릭터 전방으로 120만큼 떨어진 위치를 Start로 잡고 해당 위치에서 위로 160거리만큼의 공간을 충돌검사로 체크하여 충돌이 일어나는지 판별한다. 충돌이 없을때에만 True를 반환한다.
- const FVector CheckEnd = CheckLocation + (FVector::DownVector * 250.f);
FHitResult LedgeHit;
const bool bHitLedgeGround = GetWorld()->LineTraceSingleByChannel(LedgeHit, CheckLocation, CheckEnd, ECC_WorldStatic, ClimbQueryParams);
return bHitLedgeGround && LedgeHit.Normal.Z >= GetWalkableFloorZ();
매개변수로 주어진 위치를 기준으로 250만큼 아래쪽까지의 공간을 체크하여 충돌이 일어나는지 살펴보고 충돌이 일어나면 걸을 수 있는 위치인지를 체크하는 함수. 250까지의 길이가 조금 길어보이는데 이는 기울어진 평면일 경우도 상정했기때문.
- 앞에서 Climbing의 경우 이동을 위해서 Rotation을 수정하여 사용했는데 RootMotion이 실행 중이라면 Rotation을 수정하지 않고 RootMotion의 Rotation을 따라야하므로 해당 코드를 삽입한다
8. Climbing Dash
대쉬를 할때 케릭터는 대단히 빠른속도로 움직인다. 이 부분을 구현하기 위해서 임의로 속도를 조절하는 방법을 취할 수 있는데 애니메이션과 결합된 움직임으로 자연스러움을 연출해야하므로 수치를 조절을 하는게 쉽지 않다. Root Animation을 사용할 수 있지만 이 경우에는 수정사항이 생길경우 Animation을 수정해야하는 불편함이 있기때문에 이러한 문제점들을 해결하기 위해 CurveFloat을 이용하여 Dash를 조절한다.
* 참고 글과 Curve의 모양이 조금 다르지만 의도대로 순간적으로 빠르게 속도를 높이고 내리기때문에 동작에 이상은 없었다.
- TryClimbDashing은 Climb Dash를 시작할때 호출하는 함수로 Climb Dash에서 사용할 값들을 초기화한다.
- StoreClimbDashDirection에서는 가속한계치보다 가속이 클때에만 ClimbDashDirection을 가속과 같은 방향으로 하는데, 이건 CurveFloat을 통해서 가속 수치를 상당히 높게 주었기때문에 Climb과 ClimbDash가 이걸로 구분이 가능하다.
- CurveFloat의 Maxtime과 Mintime을 가져와서 Dash가 MaxTime만큼만 지속될 수 있도록하고 그 이상 진행하려하면 종료시킨다.
이때 Maxtime은 Dash Animation보다는 짧은 시간으로 설정되어있다.
- const float CurrentCurveSpeed = ClimbDashCurve->GetFloatValue(CurrentClimbDashTime);
Velocity = ClimbDashDirection * CurrentCurveSpeed;
Dash 속도 계산은 간단하게 CurveFloat에서 시간에 맞는 크기를 가져오고 방향과 곱해서 Climb Dash의 속도로 삼는다.
- 등반 돌진의 방향을 그대로 사용하면 모서리를 돌 수 없다. 따라서, 표면의 법선 벡터를 사용하여 방향을 표면에 정렬해야 한다. 표면의 법선 벡터를 사용하여 평면을 생성한 다음, 등반 돌진의 방향을 이 평면에 투영을 진행하고 이때, 표면의 법선 벡터의 Z 성분은 고려하지 않는다.
- 등반 돌진의 방향은 내가 올라가고 있는 평면을 기준으로 구성되어 있기때문에 내가 달라붙는 평면이 달라지면 축의 구성이 바뀌고 방향이 달라진다. 따라서 돌진시에 무시할 수 있는 Z값은 무시하고 X, Y로만 이루어진 평면으로 사영을 시켜서 평면이 달라지더라도 부드럽게 전환이 가능하도록 하기 위하여 투영을 진행하는 것.
- 이 공식이 회전이 가능하도록 방향을 구하는 것이 아니다. 앞에서 진행했던 벽과의 충돌을 점검하는 함수에서 충돌 갯수의 평균을 이용해 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시스템(젤다의 전설 벽타기) -2 (1) | 2023.09.28 |
BOTW Climbing시스템(젤다의 전설 벽타기) -1 (0) | 2023.09.28 |