갠소롱 2025. 4. 16. 07:36

🎮 Unreal Engine: 캐릭터 회피(Dodge) 방향을 캐릭터 기준으로 처리하는 방법

게임에서 회피(Dodge)를 구현할 때, 단순히 입력 방향대로 움직이게 하면 자연스럽지 않을 수 있다.
특히, 카메라 기준 이동이 적용된 게임에서는 입력 방향과 캐릭터가 실제로 바라보는 방향이 다를 수 있기 때문이다.

이번 글에서는 Unreal Engine에서 "카메라 기준 입력 방향"을 이용해 캐릭터 기준 회피 방향을 판단하는 방식을 자세히 정리한다.


🧠 문제 상황 정리

  • 카메라는 정면을 보고 있다.
  • 플레이어는 D 키를 눌러 캐릭터를 오른쪽으로 이동시킨다.
  • 이때 캐릭터는 오른쪽을 바라보며 이동한다.

이 상태에서 대시(Dodge) 키를 누르면?

  • 단순히 D 키(오른쪽 입력) 기준으로 판단하면, 캐릭터는 자신의 오른쪽(=카메라 기준 뒤쪽)으로 대시하게 된다.
  • 하지만 실제로는 캐릭터가 이미 오른쪽을 바라보고 있으므로, 캐릭터 기준 정면으로 대시하는 것이 더 자연스럽다.
🎯 즉, 입력은 "오른쪽"이지만, 캐릭터는 이미 그쪽을 "앞"으로 인식하고 있어야 한다!

🎥 기본 개념 정리

🎮 입력 벡터 (MovementVector)

플레이어가 누르고 있는 방향키 (WASD 등)의 값이다.
보통 X = 좌우, Y = 전후 로 구성되어 있으며, 카메라 기준으로 해석된다.

🎥 카메라 기준 방향

const FRotator YawRotation(0, ControlRot.Yaw, 0);
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
  • ControlRot.Yaw카메라의 바라보는 방향(Yaw).
  • ForwardDirection: 카메라 기준 앞
  • RightDirection: 카메라 기준 오른쪽

🎯 카메라 기준 입력 → 대시 방향으로 변환

FVector DodgeDirection = ForwardDirection * MovementVector.Y + RightDirection * MovementVector.X;

이 계산을 통해, 플레이어 입력과 카메라 방향을 조합한 정확한 대시 벡터를 얻는다.

입력 이동 방향
W 앞 (Y=+1)
D 오른쪽 (X=+1)
W + D 앞 + 오른쪽 (대각선)

🧭 캐릭터 기준 방향 판단

👉 캐릭터 기준 방향 벡터

FVector CharForward = ControlledCharacter->GetActorForwardVector();
FVector CharRight = ControlledCharacter->GetActorRightVector();

👉 Dot Product (내적)을 이용한 비교

float ForwardDot = FVector::DotProduct(DodgeDirNormalized, CharForward);
float RightDot = FVector::DotProduct(DodgeDirNormalized, CharRight);

DotProduct 값은 두 벡터가 얼마나 비슷한 방향인지 나타낸다.

입력 방향 캐릭터 방향 Dot 결과 의미
정면 정면 1
정면 오른쪽 0
정면 -1

📦 최종 로직 요약

if (FMath::Abs(ForwardDot) > FMath::Abs(RightDot))
{
    DirectionTagStr = ForwardDot > 0 ? TEXT("FW") : TEXT("BW");
}
else
{
    DirectionTagStr = RightDot > 0 ? TEXT("R") : TEXT("L");
}
  • ForwardDot가 더 크면: 앞/뒤 방향 대시
  • RightDot가 더 크면: 좌/우 방향 대시

✅ 전체 코드 예시

void AMyPlayerController::Dodge()
{
    AMyCharacter* ControlledCharacter = Cast(GetPawn());
    if (!IsValid(ControlledCharacter)) return;

    FVector DodgeDirection = ControlledCharacter->GetActorForwardVector();
    FRotator ControlRot = GetControlRotation();

    FString DirectionTagStr = TEXT("None");

    if (MovementVector.SizeSquared() > 0)
    {
        const FRotator YawRotation(0, ControlRot.Yaw, 0);
        const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
        const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

        DodgeDirection = ForwardDirection * MovementVector.Y + RightDirection * MovementVector.X;
        FVector DodgeDirNormalized = DodgeDirection.GetSafeNormal();

        float ForwardDot = FVector::DotProduct(DodgeDirNormalized, ControlledCharacter->GetActorForwardVector());
        float RightDot = FVector::DotProduct(DodgeDirNormalized, ControlledCharacter->GetActorRightVector());

        if (FMath::Abs(ForwardDot) > FMath::Abs(RightDot))
        {
            DirectionTagStr = ForwardDot > 0 ? TEXT("FW") : TEXT("BW");
        }
        else
        {
            DirectionTagStr = RightDot > 0 ? TEXT("R") : TEXT("L");
        }
    }
    else
    {
        DirectionTagStr = TEXT("FW");
    }

    FString FullTagName = FString::Printf(TEXT("Player.Utilities.Dodge.%s"), *DirectionTagStr);
    FGameplayTag DodgeTag = FGameplayTag::RequestGameplayTag(FName(*FullTagName));

    FGameplayTagContainer TagContainer;
    TagContainer.AddTag(DodgeTag);

    if (AbilitySystemComponent)
    {
        AbilitySystemComponent->TryActivateAbilitiesByTag(TagContainer);
    }
}

📝 마무리

이처럼 카메라 기준 입력을 캐릭터 기준 방향으로 변환하면, 훨씬 더 직관적이고 자연스러운 회피 시스템을 만들 수 있다. 🎯