디테일한 변수를 만들자
Chooser테이블은 변수들을 어떻게 설정해서 구성하느냐에 따라서 애니메이션의 선택에 있어서 다양하고 디테일한 부분까지 건들 수 있다. 우리는 지난 글에서 WantsToMove와 같은 변수들을 임시로 캐릭터의 가속도가 0보다 크면 true가 되게끔 해서 변수를 작업했었는데 디테일하게 작업해주고 싶은 마음에 GASP에서는 어떤 변수들을 어떻게 처리하고 있는지 궁금해서 확인해 본 결과 GASP에서는 다음과 같은 변수를 사용하고 있었다.

캐릭터의 현재 속도와 Trajectory를 통해 계산된 미래의 캐릭터의 속도를 비교하여 현재 속도 보다 100이 크다고 판단되는 경우 캐릭터가 움직이기 시작한다고 판단하고 있다. 하단의 Tag를 통해서 Pivot을 원하는 상태인지를 판단하는 로직이 있는데 이 디테일은 나중에 다루기로 하고 해당 함수를 참고해서 변수들을 수정해보려고 한다. 기존의 코드를 수정해서 bWantsToMove를 다음과 같이 수정하였다.
void AT3CharacterBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
float CurrentGroundSpeed = GetVelocity().Size2D();
FVector InputVector = GetLastMovementInputVector();
float FutureSpeed = InputVector.Size2D() * (GetCharacterMovement()->MaxWalkSpeed);
PlayerInputState.bWantsToMove = (InputVector.Size()>KINDA_SMALL_NUMBER) && (FutureSpeed >= (CurrentGroundSpeed +100));
const float MoveThreshold = 3.0f;
PlayerInputState.bIsMoving = CurrentGroundSpeed > MoveThreshold;
PlayerInputState.CurrentSpeed = CurrentGroundSpeed;
PlayerInputState.bIsInAir = GetCharacterMovement()->IsFalling();
}
미래 캐릭터의 속도를 담는 FutureSpeed는 현재 캐릭터의 최대 속도(MaxWalkSpeed)에 인풋 벡터의 크기를 곱해서 계산을 하게 된다. 이렇게 계산된 값이 현재 속도에 100을 더한 값보다 크고 입력 자체가 유효하면 bWantsToMove가 true가 되게끔 하였다.
다음으로 작업할 변수는 IsLand이다. 캐릭터가 낙하하고 있는 상태를 표시하는 IsFalling과는 다르게 해당 변수는 캐릭터가 낙하상태에서 OnGround로 전환되는 순간 true 되어 낙하 애니메이션을 Chooser가 선택하게 하기 위해 필요한 변수이다. 이 변수는 어떻게 작업해야 할까? GASP에서는 PlayLand라는 함수를 통해서 해당 변수를 관리하고 있으며 다음과 같이 작업되어 있다.

캐릭터의 마지막 프레임에서의 상태가 공중이면서 현재 캐릭터는 지상인 경우에만 true가 되어 Land 애니메이션을 가져올 수 있도록 되어 있다. 우리는 비슷한 함수가 C++에 존재한다. ACharacter를 상속받는 클래스에는 내부적으로 MovementMode가 변경될때마다 자동으로 호출되어 이전 상태를 뱉어내는 함수가 존재하는데 OnMovementModeChanged 함수이다. 이 함수를 오버라이드 하여 이전 상태와 지금 상태를 비교해서 착지 상태이면 IsLand변수를 true로 하는 로직을 짜면 된다.
void AT3CharacterBase::OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode)
{
Super::OnMovementModeChanged(PrevMovementMode, PreviousCustomMode);
bool bCurrentOnGround = GetCharacterMovement()->IsMovingOnGround();
if (PrevMovementMode == MOVE_Falling && bCurrentOnGround)
{
PlayerInputState.bIsJustLanded = true;
FTimerHandle LandTimerHandle;
GetWorld()->GetTimerManager().SetTimer(LandTimerHandle, [this]()
{
PlayerInputState.bIsJustLanded = false;
}, 0.2f, false);
}
}
다음처럼 로직을 구성하고 거기에 0.2초 뒤 자동으로 false로 바뀔 수 있도록 세팅까지 해주었다. 이러면 착지 순간에만 true로 바뀌고 이후에 false로 변경되어 애니메이션이 중복되서 선택되는 일은 없어질 것이다.
GaitState
이번에는 플레이어 캐릭터의 상태를 GaitState라는 이넘을 만들어 저장할 것이다. 예컨대 현재 최대 Walk Speed가 400의 속도보다 빠르면 Run, 400을 넘지 않는다면 Walk로 이 넘을 변화 하여 해당 상태에 따라 Chooser가 선택을 할 수 있게끔 작성해 준다. 이는 매 Tick마다 검사해 주어 Chooser의 선택을 돕게끔 하자
//T3PlayerInputState.h
UENUM(BlueprintType)
enum class EGaitState : uint8
{
Idle,
Walk,
Run
};
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PlayerInputState")
EGaitState T3GaitState = EGaitState::Idle;
//T3CharacterBase.cpp
void AT3CharacterBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
float CurrentGroundSpeed = GetVelocity().Size2D();
FVector InputVector = GetLastMovementInputVector();
float FutureSpeed = InputVector.Size2D() * (GetCharacterMovement()->MaxWalkSpeed);
PlayerInputState.bWantsToMove = (InputVector.Size()>KINDA_SMALL_NUMBER) && (FutureSpeed >= (CurrentGroundSpeed +100));
const float MoveThreshold = 3.0f;
PlayerInputState.bIsMoving = CurrentGroundSpeed > MoveThreshold;
PlayerInputState.CurrentSpeed = CurrentGroundSpeed;
PlayerInputState.bIsInAir = GetCharacterMovement()->IsFalling();
if (GetCharacterMovement()->MaxWalkSpeed > 400.0f)
{
PlayerInputState.T3GaitState = EGaitState::Run;
}
else
{
PlayerInputState.T3GaitState = EGaitState::Walk;
}
}
Chooser 작성 후 테스팅
전체적으로 State로 인한 전환은 되는 모습을 보이지만 애니메이션이 재생되는 중간 중간 다른 애니메이션이 선택되어 블랜딩이 이상하게 되는 모습이 간간히 보임을 확인할 수 있다. 일단은 영상에서 처럼 디버깅을 통해서 어떠한 PSD가 선택되어 애니메이션이 재생되는지 파악하는게 우선이다. 특정 변수로 인해서 PSD가 선택되지 않는 경우 변수의 조건이나 변수가 변하는 타이밍 또는 새로운 변수를 추가하여서 선택을 좀더 디테일하게 다루어주면서 수정해준다.
이러한 부분은 어떻게 수정해야 할까?
해당 부분은 특정 애니메이션이 선택되면 해당 애니메이션의 재생이 종료될 때 까지 다른 애니메이션을 선택하지 못하도록 하면 된다. 이 기능은 Motion Matching의 Set Database To Search 노드에서 interrupt Mode를 통해서 조절할 수 있는데 우리는 지금 기본적으로 interrupt on Database Change로서 DataBase가 바뀌면 새로 선택된 애니메이션과 기존의 애니메이션을 적절히 블랜드 하는 옵션을 사용 중이다. GASP에서는 Custom Interrupt를 작성해서 필요할 때 해당 기능을 껐다 켜면서 조절하고 있는데 다음에는 이걸 한번 다루어 보려고 한다.
'unreal 5기' 카테고리의 다른 글
| 260202 언리얼엔진 본캠프 120일차 피격 Motion을 구현하자 (0) | 2026.02.02 |
|---|---|
| 260122 언리얼엔진 본캠프 113일차 GASP(7) (0) | 2026.01.22 |
| 260120 언리얼엔진 본캠프 111일차 GASP(6) ChooserTable-2 (0) | 2026.01.20 |
| 260116 언리얼엔진 본캠프 109일차 GASP(5) ChooserTable-1 (1) | 2026.01.16 |
| 260115 언리얼엔진 본캠프 109일차 GASP(4) MotionMatching (1) | 2026.01.15 |