Team과 Affiliation 시스템
IGenericTeamAgentInterface, TeamId, 적/아군/중립 판별 시스템을 구현합니다
IGenericTeamAgentInterface
팀 시스템의 핵심 인터페이스
IGenericTeamAgentInterface는 UE5의 팀 시스템 인터페이스입니다. AAIController가 기본 구현하며, PlayerController나 커스텀 액터에도 구현 가능합니다. Perception의 Affiliation 필터링과 직접 연동됩니다.
// 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가 플레이어를 적/아군으로 판별할 수 있습니다.
Attitude 매트릭스 커스터마이징
팀 간 관계 정의 커스터마이징
기본 Attitude 로직은 "같은 TeamID = 아군, 다른 TeamID = 적"입니다. GetTeamAttitudeTowards()를 오버라이드하면 복잡한 팀 관계(동맹, 배신 등)를 구현할 수 있습니다.
// 기본 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 진영이 바뀌는 게임에 유용합니다.
Perception과 Team 연동
DetectionByAffiliation 필터링 메커니즘
각 Sense Config의 DetectionByAffiliation은 Team 시스템의 Attitude 결과를 기반으로 감지 대상을 필터링합니다. 적만 감지, 아군도 감지, 중립만 감지 등 유연한 설정이 가능합니다.
// 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 필터가 계속 적용됩니다.
실전 팀 시스템 설계
게임 팩션, 진영 전환, 중립 NPC 패턴
실전 게임에서는 단순한 2팀 구조를 넘어, 다수 진영과 동적 관계 변화를 구현해야 합니다. 팩션 시스템과 Team 시스템을 결합한 패턴을 살펴봅니다.
// 팩션 시스템과 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으로 구현하면 레벨 전환 시에도 팩션 관계가 유지됩니다. 퀘스트 완료로 산적이 아군이 되는 등의 동적 변화를 쉽게 관리할 수 있습니다.
핵심 요약
- IGenericTeamAgentInterface는 팀 시스템의 핵심이며, AIController는 기본 구현하지만 PlayerController는 직접 구현해야 한다
- GetTeamAttitudeTowards()를 오버라이드하여 복잡한 다진영 관계 매트릭스를 구현할 수 있다
- DetectionByAffiliation이 Perception Sense의 감지 대상 필터링과 직접 연동된다
- 런타임 팀 변경 시 반드시 Perception 시스템에 UpdateListener를 호출해야 변경이 반영된다
도전 과제
배운 내용을 직접 실습해보세요
IGenericTeamAgentInterface를 구현하여 플레이어(Team 0)와 적(Team 1)을 구분하세요. GetTeamAttitudeTowards()로 적/아군 판별이 Perception 필터링에 반영되는지 확인합니다.
FAISenseAffiliationFilter에서 DetectEnemies, DetectNeutrals, DetectFriendlies를 각각 토글하며 AI의 감지 대상 변화를 테스트하세요.
게임플레이 중 NPC의 팀을 동적으로 변경하는 시스템을 구현하세요. 중립 NPC가 플레이어의 행동에 따라 아군 또는 적으로 전환되며, 다른 AI의 Perception이 즉시 업데이트되도록 합니다.