
기존 AI는 플레이어와 거리가 가까우면 바로 공격했다.
문제는 플레이어 방향을 보고 있지 않아도 공격이 실행된다는 점이었다.
그래서 공격 전에 반드시 회전을 수행하도록 구조를 수정했다.
기존 흐름:
Move -(일정 거리 이내)-> Attack -> Move
수정 후:
Rotate -> Move -> Attack -> Rotate
공격이 끝나면 다시 Rotate State로 돌아가 플레이어 방향을 먼저 바라본 뒤, 이동, 공격을 진행하도록 구성했다.
회전 완료 여부는:
두 벡터의 내적값을 이용해 계산했다.
//RotateToTargetTask.cpp
FVector ForwardDirection = ContextActor->GetActorForwardVector();
FVector ToTargetDirection = (TargetActor->GetActorLocation() - ContextActor->GetActorLocation());
ToTargetDirection = ToTargetDirection.GetSafeNormal();
float DotResult = FVector::DotProduct(ForwardDirection, ToTargetDirection);
그리고 Data Asset에 RotationThreshold 값을 두고,
해당 각도 이내면 회전 완료로 처리했다.
//RotateToTargetTask.cpp
float Threshold = FMath::Cos(FMath::DegreesToRadians(RotateThreshold));
return DotResult > Threshold ? true : false;
이 판정은 Tick에서 계속 확인하도록 구현했다.
다만 처음에는 Tick이 호출되지 않았는데,
생성자에서 Tick 활성화를 하지 않은 것이 원인이었다.
URotateToTargetTask::URotateToTargetTask(const FObjectInitializer& ObjectInitializer) :
Super(ObjectInitializer)
{
bShouldCallTick = true;
bShouldCallTickOnlyOnEvents = true;
}
설정 이후 정상적으로 공격 후 플레이어 방향으로 회전을 하였다. 그러나 또 문제가 발생하였다
이후 회전 자체는 정상적으로 수행됐지만,
플레이어 근처에서는 계속 Rotate State에 머무르는 문제가 발생했다.
원인은 방향 벡터 계산에 있었다.
나는 처음에:
이 둘이 같은 평면 위에 있다고 생각했다.
하지만 실제로는:
상태였다.
즉, 위에서 보면 같은 방향이어도
실제 3D 벡터 기준으로는 각도 차이가 발생하고 있었던 것이다.
![]() | ![]() |
|---|
현재 게임은 평지 기반 구조였고,
중요한 것은 높낮이가 아니라 “적이 플레이어를 바라보는가”였다.
그래서 ToTargetDirection을 XY 평면에 정사영한 뒤 사용하도록 수정했다.
ToTargetDirection.Z = 0.f;
ToTargetDirection.Normalize();
이후 해당 벡터와 Forward Vector를 내적해 방향 차이를 계산했다.
이 방식을 선택한 이유는
단순히 Threshold를 늘리는 방식은 몬스터 크기에 따라 문제가 생겼기 때문이다.
예를 들어 크기가 큰 몬스터는 Threshold를 지나치게 크게 잡아야 했고,
결국 몬스터마다 값을 따로 조정해야 하는 상황이 발생했다.
반면 XY 평면 기준으로 계산하니
몬스터 크기와 관계없이 안정적으로 방향 판정이 가능했다.
결과적으로:
문제를 해결할 수 있었다.