TIL_088 : AI 추격 트러블 슈팅, 인터페이스, Cast/static_cast

김펭귄·2025년 12월 24일

Today What I Learned (TIL)

목록 보기
88/139

1. AI Chasing 트러블 슈팅

1.1. 문제 상황

  • 영상처럼 플레이어가 AI의 시야범위에 들어가면 AI는 캐릭터를 추격함

  • 하지만 잘 추격하다가도 어느 순간 추격을 멈추는 상황이 발생하는 문제가 생겼음

1.2. 해결과정

  • AI의 State Tree에서 Chasing State가 유지되지 않는 것 같다고 판단하여 먼저 Rewind Debugger를 통해 확인해보았음

  • 하지만, AI가 이동을 멈춘 상태에서도 여전히 Chasing State를 유지하는 것이 확인되었음

  • 현재 Move To Target State의 Task도 문제 없는 것 같아 State의 문제는 아니다고 판단하여 Rewind Debugger로 다른 부분도 확인해봄

  • AI의 이동이 멈추는 순간부터, Debugger의 Navigation부분에서 변동이 생기는 것을 확인

  • Navigation에 문제가 있을 것 같다고 생각하여 AI의 NavMesh를 확인하였음

  • 추측대로, Chasing State는 유지되나 NavMesh가 끊겨 있어 이동이 불가능했던 것

  • 레벨에서 NavMesh의 크기를 확장시켜 문제를 해결하였음

2. 언리얼 인터페이스

2.1. 인터페이스 선언

  • 에디터에서 인터페이스를 부모클래스로 하여 새 인터페이스를 생성
#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "ActivateInterface.generated.h"

UINTERFACE(MinimalAPI)
class UActivateInterface : public UInterface
{
	GENERATED_BODY()	// 여기는 내버려둠
};

class TARCOPY_API IActivateInterface
{
	GENERATED_BODY()
    
public:
	virtual void Activate(AActor* InInstigator) = 0;	// 순수가상함수
};
  • IActivateInterface 여기에만 추가 구현

2.2. 인터페이스 상속받아 실제 구현

// MyChest.h
class AMyChest : public AActor, public IInteractable 
{
    GENERATED_BODY()
public:
    // 오버라이드 시 _Implementation을 붙입니다.
    virtual void Activate_Implementation(AActor* InInstigator) override;
};

// MyChest.cpp
void AMyChest::Activate_Implementation(AActor* InInstigator) {
    UE_LOG(LogTemp, Log, TEXT("Activated by %s"), *InInstigator->GetName());
}
  • 부모로 인터페이스 추가

  • 구현 시, _Implementation 붙여 사용

2.3. 인터페이스 객체 사용

if (OtherActor->GetClass()->ImplementsInterface(UMyInterface::StaticClass()))
{
    IMyInterface::Execute_Activate(OtherActor, this);
}
  • UObject 시스템을 이용해, 인터페이스를 가지고 있는 객체인지 먼저 확인

  • 그리고 Execute_함수명으로 실행. 매개인자(Instigator)는 뒤에 넣어주면 된다

  • 블루프린트와의 호환성을 위해 위와같이 작성하고, C++에서만 인터페이스 사용하면 다음과 같이 작성

2.4. C++ 전용 인터페이스

// MyInterface.h
class IMyInterface
{
    GENERATED_BODY()

public:
    // UFUNCTION()을 붙이지 않고, 순수 가상 함수로 선언
    virtual void Activate(AActor* InInstigator) = 0;
};
// MyActor.h
#include "MyInterface.h"

class MYPROJECT_API AMyActor : public AActor, public IMyInterface
{
	GENERATED_BODY()

public
	// _Implementation 없이 그냥 구현
	virtual void Activate(AActor* InInstigator) override;
};
// MyActor.cpp
void AMyActor::Activate(AActor* InInstigator)
{
    UE_LOG(LogTemp, Warning, TEXT("Activate"));
}
// 호출부
// 인터페이스로 업캐스팅
IMyInterface* InterfaceTarget = Cast<IMyInterface>(OtherActor);

if (InterfaceTarget)
{
    // 직접 호출 (가장 빠르고 간결함)
    InterfaceTarget->Activate(this);
}

3. 언리얼 Cast vs static_cast

3.1. Cast

T* 변수명 = Cast<T>(변수)

  • const가 붙은 변수를 변환할 때는 const T*로 해줘야함

  • 보통 UObject의 포인터 변환에 사용

3.2. static_cast

  • 그래서 포인터가 아니거나 구조체같은 UObject가 아닐 땐 static_cast사용
if (DamageEvent.IsOfType(FPointDamageEvent::ClassID)) 
{
   const FPointDamageEvent& D = static_cast<const FPointDamageEvent&>(DamageEvent);  
}
  • IsOfType처럼 캐스팅 가능한지 먼저 판단해주기

  • static_cast로 변환하기

  • dynamic_cast는 RTTI 오버헤드가 커서 사용추천 안 함

profile
반갑습니다

0개의 댓글