PART 3 - 강의 3/3

Team과 Affiliation 시스템

IGenericTeamAgentInterface, TeamId, 적/아군/중립 판별 시스템을 구현합니다

01

IGenericTeamAgentInterface

팀 시스템의 핵심 인터페이스

IGenericTeamAgentInterface는 UE5의 팀 시스템 인터페이스입니다. AAIController가 기본 구현하며, PlayerController나 커스텀 액터에도 구현 가능합니다. Perception의 Affiliation 필터링과 직접 연동됩니다.

C++// IGenericTeamAgentInterface 핵심 메서드 class IGenericTeamAgentInterface { // 팀 ID 설정/조회 virtual void SetGenericTeamId(const FGenericTeamId& TeamID); virtual FGenericTeamId GetGenericTeamId() const; // 다른 액터와의 관계 판별 virtual ETeamAttitude::Type GetTeamAttitudeTowards( const AActor& Other) const; }; // ETeamAttitude 열거형 enum class ETeamAttitude : uint8 { Friendly, // 아군 Neutral, // 중립 Hostile, // 적 }; // FGenericTeamId - 팀 식별자 struct FGenericTeamId { uint8 TeamID; // 0~255 static const FGenericTeamId NoTeam; // 255 = 팀 없음 }; // AIController에서 팀 설정 AMyAIController::AMyAIController() { // 팀 1 = 적 AI SetGenericTeamId(FGenericTeamId(1)); } // PlayerController에서 팀 설정 (인터페이스 구현 필요) class AMyPlayerController : public APlayerController, public IGenericTeamAgentInterface { FGenericTeamId TeamId = FGenericTeamId(0); // 플레이어 팀 virtual FGenericTeamId GetGenericTeamId() const override { return TeamId; } virtual void SetGenericTeamId( const FGenericTeamId& NewTeamID) override { TeamId = NewTeamID; } };
주의

AAIController는 이미 IGenericTeamAgentInterface를 구현합니다. 하지만 APlayerController는 기본 구현이 없으므로 직접 인터페이스를 구현해야 AI가 플레이어를 적/아군으로 판별할 수 있습니다.

02

Attitude 매트릭스 커스터마이징

팀 간 관계 정의 커스터마이징

기본 Attitude 로직은 "같은 TeamID = 아군, 다른 TeamID = 적"입니다. GetTeamAttitudeTowards()를 오버라이드하면 복잡한 팀 관계(동맹, 배신 등)를 구현할 수 있습니다.

C++// 기본 Attitude 로직 (AAIController) ETeamAttitude::Type AAIController::GetTeamAttitudeTowards( const AActor& Other) const { const IGenericTeamAgentInterface* OtherTeamAgent = Cast<IGenericTeamAgentInterface>(&Other); if (!OtherTeamAgent) return ETeamAttitude::Neutral; FGenericTeamId OtherTeamId = OtherTeamAgent->GetGenericTeamId(); // NoTeam은 항상 Neutral if (GetGenericTeamId() == FGenericTeamId::NoTeam || OtherTeamId == FGenericTeamId::NoTeam) return ETeamAttitude::Neutral; // 같은 팀 = Friendly, 다른 팀 = Hostile return GetGenericTeamId() == OtherTeamId ? ETeamAttitude::Friendly : ETeamAttitude::Hostile; } // 커스텀 Attitude 매트릭스 구현 class AMyAIController : public AAIController { virtual ETeamAttitude::Type GetTeamAttitudeTowards( const AActor& Other) const override { const IGenericTeamAgentInterface* OtherAgent = Cast<IGenericTeamAgentInterface>(&Other); if (!OtherAgent) return ETeamAttitude::Neutral; uint8 MyTeam = GetGenericTeamId().GetId(); uint8 OtherTeam = OtherAgent->GetGenericTeamId().GetId(); // 팀 0(플레이어) - 팀 1(마을 NPC): 아군 // 팀 0(플레이어) - 팀 2(몬스터): 적 // 팀 1(마을 NPC) - 팀 2(몬스터): 적 // 팀 2(몬스터) - 팀 3(언데드): 중립 static const ETeamAttitude::Type AttitudeMatrix[4][4] = { // 팀0 팀1 팀2 팀3 {Friendly, Friendly, Hostile, Hostile }, // 팀0 {Friendly, Friendly, Hostile, Hostile }, // 팀1 {Hostile, Hostile, Friendly, Neutral }, // 팀2 {Hostile, Hostile, Neutral, Friendly}, // 팀3 }; if (MyTeam < 4 && OtherTeam < 4) return AttitudeMatrix[MyTeam][OtherTeam]; return ETeamAttitude::Neutral; } };

Attitude 매트릭스를 DataAsset이나 DataTable로 관리하면, 런타임에 동맹 관계를 동적으로 변경할 수 있습니다. 퀘스트 진행에 따라 NPC 진영이 바뀌는 게임에 유용합니다.

03

Perception과 Team 연동

DetectionByAffiliation 필터링 메커니즘

각 Sense Config의 DetectionByAffiliation은 Team 시스템의 Attitude 결과를 기반으로 감지 대상을 필터링합니다. 적만 감지, 아군도 감지, 중립만 감지 등 유연한 설정이 가능합니다.

C++// DetectionByAffiliation 설정 FAISenseAffiliationFilter AffiliationFilter; AffiliationFilter.bDetectEnemies = true; // 적 감지 AffiliationFilter.bDetectNeutrals = false; // 중립 무시 AffiliationFilter.bDetectFriendlies = false; // 아군 무시 SightConfig->DetectionByAffiliation = AffiliationFilter; // Perception 콜백에서 Affiliation 활용 void AMyAIController::OnTargetPerceptionUpdated( AActor* Actor, FAIStimulus Stimulus) { if (!Stimulus.WasSuccessfullySensed()) return; // 팀 태도 확인 ETeamAttitude::Type Attitude = GetTeamAttitudeTowards(*Actor); switch (Attitude) { case ETeamAttitude::Hostile: // 적 발견 → 전투 모드 GetBlackboardComponent()->SetValueAsObject( TEXT("TargetEnemy"), Actor); GetBlackboardComponent()->SetValueAsBool( TEXT("IsInCombat"), true); break; case ETeamAttitude::Neutral: // 중립 발견 → 경계 모드 GetBlackboardComponent()->SetValueAsBool( TEXT("IsAlert"), true); break; case ETeamAttitude::Friendly: // 아군 발견 → 협동 가능 판단 GetBlackboardComponent()->SetValueAsObject( TEXT("NearbyAlly"), Actor); break; } } // 동적 팀 변경 시 Perception 갱신 void AMyAIController::ChangeTeam(uint8 NewTeamID) { SetGenericTeamId(FGenericTeamId(NewTeamID)); // Perception 시스템에 변경 알림 if (UAIPerceptionSystem* PerceptionSys = UAIPerceptionSystem::GetCurrent(GetWorld())) { PerceptionSys->UpdateListener( *GetPerceptionComponent()); } }
주의

런타임에 TeamId를 변경한 후에는 반드시 Perception 시스템에 알려야 합니다. 그렇지 않으면 이전 팀 기준의 Affiliation 필터가 계속 적용됩니다.

04

실전 팀 시스템 설계

게임 팩션, 진영 전환, 중립 NPC 패턴

실전 게임에서는 단순한 2팀 구조를 넘어, 다수 진영과 동적 관계 변화를 구현해야 합니다. 팩션 시스템과 Team 시스템을 결합한 패턴을 살펴봅니다.

C++// 팩션 시스템과 Team 연동 UENUM(BlueprintType) enum class EFaction : uint8 { Player = 0, Village = 1, Bandits = 2, Undead = 3, Wildlife = 4, }; // 팩션 매니저 (게임 인스턴스 서브시스템) UCLASS() class UFactionSubsystem : public UGameInstanceSubsystem { GENERATED_BODY() public: // 두 팩션 간의 관계 조회 UFUNCTION(BlueprintCallable) ETeamAttitude::Type GetAttitude( EFaction FactionA, EFaction FactionB) const; // 런타임에 관계 변경 (퀘스트 등) UFUNCTION(BlueprintCallable) void SetAttitude(EFaction FactionA, EFaction FactionB, ETeamAttitude::Type NewAttitude); // 관계 변경 이벤트 DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams( FOnAttitudeChanged, EFaction, FactionA, EFaction, FactionB, ETeamAttitude::Type, NewAttitude); FOnAttitudeChanged OnAttitudeChanged; private: // Attitude 매트릭스 (DataTable 기반) TMap<TPair<EFaction,EFaction>, ETeamAttitude::Type> AttitudeMap; }; // AIController에서 팩션 시스템 활용 ETeamAttitude::Type AMyAIController::GetTeamAttitudeTowards( const AActor& Other) const { UFactionSubsystem* Factions = GetGameInstance()->GetSubsystem<UFactionSubsystem>(); EFaction MyFaction = GetMyFaction(); EFaction OtherFaction = GetFactionOf(&Other); return Factions->GetAttitude(MyFaction, OtherFaction); }
설계

팩션 시스템을 GameInstance Subsystem으로 구현하면 레벨 전환 시에도 팩션 관계가 유지됩니다. 퀘스트 완료로 산적이 아군이 되는 등의 동적 변화를 쉽게 관리할 수 있습니다.

SUMMARY

핵심 요약

  • IGenericTeamAgentInterface는 팀 시스템의 핵심이며, AIController는 기본 구현하지만 PlayerController는 직접 구현해야 한다
  • GetTeamAttitudeTowards()를 오버라이드하여 복잡한 다진영 관계 매트릭스를 구현할 수 있다
  • DetectionByAffiliation이 Perception Sense의 감지 대상 필터링과 직접 연동된다
  • 런타임 팀 변경 시 반드시 Perception 시스템에 UpdateListener를 호출해야 변경이 반영된다
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: Team 설정 구현

IGenericTeamAgentInterface를 구현하여 플레이어(Team 0)와 적(Team 1)을 구분하세요. GetTeamAttitudeTowards()로 적/아군 판별이 Perception 필터링에 반영되는지 확인합니다.

실습 2: Affiliation 필터 활용

FAISenseAffiliationFilter에서 DetectEnemies, DetectNeutrals, DetectFriendlies를 각각 토글하며 AI의 감지 대상 변화를 테스트하세요.

심화 과제

게임플레이 중 NPC의 팀을 동적으로 변경하는 시스템을 구현하세요. 중립 NPC가 플레이어의 행동에 따라 아군 또는 적으로 전환되며, 다른 AI의 Perception이 즉시 업데이트되도록 합니다.