
특정 액터를 바라보게 하고 싶으면 AIController의 SetFocus를 불러주면 된다
CachedAIController->SetFocus(TargetActor);
이때, 두 번째 인자로 Prioritiy를 넣어줄 수 있다.
namespace EAIFocusPriority
{
typedef uint8 Type;
inline const Type Default = 0;
inline const Type Move = 1;
inline const Type Gameplay = 2;
inline const Type LastFocusPriority = Gameplay;
}
void AAIController::SetFocus(AActor* NewFocus, EAIFocusPriority::Type InPriority)
{
// clear out existing
ClearFocus(InPriority);
// now set new
if (NewFocus)
{
if (InPriority >= FocusInformation.Priorities.Num())
{
FocusInformation.Priorities.SetNum(InPriority + 1);
}
FocusInformation.Priorities[InPriority].Actor = NewFocus;
}
}
이때, 두 번째 인자 (Priority)를 안 넣어주면 기본적으로 EAIFocusPriority::Gameplay이다. 그래서 ClearFocus 때도 이 인자를 넣어주어 없애야 함. 첨에 Default로 했다가 안 되어서 고생했음.
CachedAIController->ClearFocus(EAIFocusPriority::Gameplay);
호스트에서는 회전 애니메이션이 정상 동작했지만,
클라이언트에서는 회전 애니메이션 없이 몸만 회전하는 문제가 발생했다.
원인으로는 회전을 할 때, AIController의 회전값을 가져와서 회전을 하는데 클라이언트에는 AIController가 존재하지 않았고, Direction 계산 자체가 수행되지 않았다.
추가로 BlendSpace 기반 회전도 문제가 있었다.
현재 방식은:
두 벡터의 각 차이(Direction)를 구하고, 이를 회전해야하는 값으로 BlendSpace에 넣어주어 회전시켜주었다.

Controller Rotator와 Forward Vector가 동일하다. Forward Vector는 그대로이고, Controller Rotator는 타겟을 향하게 되며 두 벡터 사이의 각 차이(Direction)가 발생한다.![]() | ![]() |
|---|
문제는 적이 타겟을 향하며 회전이 거의 끝나갈수록 각 차이가 작아지고,
BlendSpace 입력값도 작아지면서 회전 애니메이션이 거의 멈춰 보였다는 점이다.
결국:
“회전은 방향만 있을 뿐 값이 필요 없는데, BlendSpace 값 기반으로 제어하는 방식이 맞지 않는다”
고 판단했다.
처음에는 회전 애니메이션도 GAS로 처리하려 했다.
동기화가 문제였으니 동기화만 해결하기 위해 무작정 GAS를 생각했던 것이었다.
문제는 회전 도중 상태가 바뀌는 경우였다.
예를 들어:
같은 상황이 발생하면:
등 추가 작업이 계속 필요해졌다.
결국:
“현재 상태에 따른 애니메이션은 Animation Blueprint가 가장 적합하다”
고 판단했다.
최종적으로는 회전 자체를 ABP State 기반으로 다시 구성했다.
호스트에서는:
만 수행했다.
// RotateToTargetTask.cpp
float DotResult = FVector::DotProduct(ForwardDirection, ToTargetDirection); // 각도 판단
float CrossResult = FVector::CrossProduct(ToTargetDirection, ForwardDirection).Z; // 좌우 판단
float Threshold = FMath::Cos(FMath::DegreesToRadians(RotateThreshold));
if (DotResult < Threshold)
{
ContextActor->bIsTurning = true;
ContextActor->bIsTurningLeft = (CrossResult > 0);
}
그리고 이 두 변수 값만 동기화했다.
// BaseEnemyCharacter.h
UPROPERTY(BlueprintReadOnly, Replicated)
uint8 bIsTurning : 1;
UPROPERTY(BlueprintReadOnly, Replicated)
uint8 bIsTurningLeft : 1;
ABP에서는:
bIsTurning == true면 Rotate State 진입bIsTurningLeft에 따라 좌/우 회전 애니메이션 재생하도록 구성했다.


ABP로 State 별로 알맞은 애니메이션 재생이 가능해졌으며, 동기화 문제도 해결하였다.
다만 이후 네트워크 지연도 고민하게 됐다.
현재 구조는:
에 따라 클라이언트 회전 애니메이션 시작 타이밍이 달라질 수 있었다.
결국:
“애니메이션 회전량과 실제 캐릭터 회전량이 어긋날 가능성”
이 존재했다.
그래서:
하는 구조도 고민했다.
즉:
SetFocus()와 Movement 기반으로 실제 Rotation 동기화하는 방식이다.
하지만 테스트 결과 현재 Root Motion 기반 회전이 충분히 자연스러웠고,
회전 시간과 회전 각도를 데이터 에셋으로 맞춰주니 큰 문제 없이 동작했다.
결과적으로 현재는:
회전 상태는 ABP가 관리하고,
회전 방향 동기화는 최소 bool 값만 복제하며,
실제 회전 연출은 RM 기반 몽타주를 사용
하는 구조로 정리했다.