▶️ 팀설정 함수 확인하기
AAIPawnBase 에서 상속받은 IGenericTeamAgentInterface 타고 FGenericTeamId 타고 들어가면
팀설정 함수 확인 가능.
- TFunction : 언리얼에서 사용하는 함수 포인터 같은 아이
#include "GenericTeamAgentInterface.h"
..
class RESTART0501_API AAIPawnBase : public APawn, public IGenericTeamAgentInterface
1. 팀 나누기
1) 팀간 관계 설정
AGameModeMain 에서
#include "GenericTeamAgentInterface.h"
...
private:
// 각 팀별로 서로 적인지, 아군인지 구분
// 첫번째 키, 두번째 키 넣으면 둘의 관계를 값으로 가짐
TMap<uint8, TMap<uint8, ETeamAttitude::Type>> TeamAttitudes;
▶️FindOrAdd()
: 제공한 Key에 연관된 값으로 레퍼런스를 반환한다.
Key가 TMap 컨테이너에 존재하지 않는 경우, 새로 생성된 엘리먼트에 Key와 기본 생성된 값을 반환하고, TMap 컨테이너에 추가한다.
TeamAttitudes.FindOrAdd( 0 ).Add( 1, ETeamAttitude::Type::Friendly );
=> 0번팀을 찾고 없으면 만들어서 반환하는데, 0번팀의 1번팀과의 관계는 친구
AGameModeKDT::InitGame() 에서 팀 설정을 해준다
- 굳이 양방향일 필요는 없다 ( 몹 - 펫 )
- Neutral 중립
// 0번팀은 1번팀을 적으로 간주
void AGameModeMain::InitGame( const FString& MapName, const FString& Options, FString& ErrorMessage )
{
Super::InitGame( MapName, Options, ErrorMessage );
TeamAttitudes.FindOrAdd( 0 ).Add( 1, ETeamAttitude::Hostile );
TeamAttitudes.FindOrAdd( 0 ).Add( 2, ETeamAttitude::Friendly );
TeamAttitudes.FindOrAdd( 1 ).Add( 0, ETeamAttitude::Hostile );
TeamAttitudes.FindOrAdd( 1 ).Add( 2, ETeamAttitude::Neutral );
TeamAttitudes.FindOrAdd( 2 ).Add( 0, ETeamAttitude::Friendly );
TeamAttitudes.FindOrAdd( 2 ).Add( 1, ETeamAttitude::Hostile );
// 팀설정
FGenericTeamId::SetAttitudeSolver(
[this] ( FGenericTeamId TeamA, FGenericTeamId TeamB )->ETeamAttitude::Type
{
uint8 teamA_ID = TeamA.GetId();
uint8 teamB_ID = TeamB.GetId();
if ( TeamAttitudes.Find( teamA_ID ) == nullptr )
return ETeamAttitude::Neutral;
if( TeamAttitudes[teamA_ID].Find( teamB_ID ) == nullptr )
return ETeamAttitude::Neutral;
return ( ETeamAttitude::Type)(* TeamAttitudes[teamA_ID].Find( teamB_ID ) );
} );
}
▶️ 람다식
익명함수, 함수객체 라고도 불림.
3가지로 이루어짐 : 캡쳐절, 매개변수 목록, 반환값
캡쳐절 : [this],
매개변수 목록 : ( FGenericTeamId TeamA, FGenericTeamId TeamB )
반환값 : ->ETeamAttitude::Type
캡쳐절
[& ] : 람다식 위에있는것(ex: 지역변수 등)을 참조로 가져오겠다
[= ] : 값을 복사하겠다.
반환값이 없을땐 -> 이후 비워두면 됨.
위에 처럼 안하고 함수 호출해도됨
ETeamAttitude::Type AGameModeKDT::TeamAttitude( FGenericTeamId TeamA, FGenericTeamId TeamB )
{
uint8 teamA_ID = TeamA.GetId();
uint8 teamB_ID = TeamB.GetId();
if ( TeamAttitudes.Find( teamA_ID ) == nullptr )
return ETeamAttitude::Neutral;
if ( TeamAttitudes[teamA_ID].Find( teamB_ID ) == nullptr )
return ETeamAttitude::Neutral;
return ( ETeamAttitude::Type )( *TeamAttitudes[teamA_ID].Find( teamB_ID ) );
}
2) 팀 설정
MonsterAIController. 생성자에서 teamId 1로 변경
SetGenericTeamId(FGenericTeamId(1));
PlayerControllerMain 생성자에서 teamId 0으로 변경
SetGenericTeamId(FGenericTeamId(0));
3) 확인용
void AMonsterAIController::OnTargetFound( AActor* Actor, FAIStimulus Stimulus )
{
ETeamAttitude::Type attitude = GetTeamAttitudeTowards( *Actor ); // 브레이크포인트 추가
…
}
=> 움직이는 캐릭터들(PlayerBase, MonsterBase) 클래스에
IGenericTeamAgentInterface 다중상속,
mTeamId 프로퍼티 추가
SetGenericTeamId(), GetLifetimeReplicatedProps(), PossessedBy() 함수들 설정 해주고
빠진거 추가하고
실행하면 1팀(몬)이 0팀(나)을 바라보는 관계가 정상적으로 나오는걸 알 수 있음
2. 팀설정 TPair방식으로 맵은 TPair사용해서 사용 해보기
TMap<TPair<uint8, uint8>, ETeamAttitude::Type> TeamAttitudes2; // 이런식으로도 사용 가능
AGameModeKDT::InitGame() 에서 팀 설정
TeamAttitudes2.Add( TPair<uint8, uint8>( 0, 1 ), ETeamAttitude::Hostile );
TeamAttitudes2.Add( TPair<uint8, uint8>( 0, 2 ), ETeamAttitude::Friendly );
TeamAttitudes2.Add( TPair<uint8, uint8>( 1, 0 ), ETeamAttitude::Hostile );
TeamAttitudes2.Add( TPair<uint8, uint8>( 1, 2 ), ETeamAttitude::Friendly );
TeamAttitudes2.Add( TPair<uint8, uint8>( 2, 0 ), ETeamAttitude::Friendly );
TeamAttitudes2.Add( TPair<uint8, uint8>( 2, 1 ), ETeamAttitude::Hostile );
FGenericTeamId::SetAttitudeSolver(
[this] ( FGenericTeamId TeamA, FGenericTeamId TeamB )->ETeamAttitude::Type
{
uint8 teamA_ID = TeamA.GetId();
uint8 teamB_ID = TeamB.GetId();
TPair<uint8, uint8> key (teamA_ID, teamB_ID );
if( !TeamAttitudes2.Find(key) )
return ETeamAttitude::Neutral;
return ( ETeamAttitude::Type)(*TeamAttitudes2.Find( key ));
});
타겟 찾았을 경우 팀 에티튜드에 따라 분기처리
void AMonsterAIController::OnTargetFound( AActor* Actor, FAIStimulus Stimulus )
{
ETeamAttitude::Type attitude = GetTeamAttitudeTowards( *Actor );
// 감지성공
if ( Stimulus.WasSuccessfullySensed() )
{
GEngine->AddOnScreenDebugMessage( 1, 10.f, FColor::Red, L"true" );
if( attitude == ETeamAttitude::Hostile || attitude == ETeamAttitude::Neutral )
{
// 기존 타겟과 비교
AActor* foundActor = Cast<AActor>( Blackboard->GetValueAsObject( L"Target" ) );
// 같은 플레이어 태그를 가졌으면 타겟 바꾸지 않기
if ( IsValid( foundActor ) )
{
if ( foundActor->ActorHasTag( L"Player" ) && Actor->ActorHasTag( L"Player" ) )
return;
}
Blackboard->SetValueAsObject( L"Target", Actor );
}
}
else
{
GEngine->AddOnScreenDebugMessage( 1, 10.f, FColor::Red, L"false" );
if ( attitude == ETeamAttitude::Hostile || attitude == ETeamAttitude::Neutral )
{
AActor* foundActor = Cast<AActor>( Blackboard->GetValueAsObject( L"Target" ) );
if ( IsValid( foundActor ) )
{
if ( foundActor->GetUniqueID() == Actor->GetUniqueID() )
Blackboard->SetValueAsObject( L"Target", nullptr );
return;
}
Blackboard->SetValueAsObject( L"Target", nullptr );
}
}
}
3. 이동 태스크 c++처리
1) moveto 상속받아서 클래스 하나 만들기
Task폴더에 BTTask_Main_MoveTo_Trace생성
2)
생성자 추가
UBTTask_MoveTo에서 ExecuteTask(), AbortTask(), OnTaskFinished(),
UBTTaskNode에서 TickTask() 가져와서 오버라이딩
class RESTART0501_API UBTTask_Main_MoveTo_Trace : public UBTTask_MoveTo
{
GENERATED_BODY()
public :
UBTTask_Main_MoveTo_Trace();
public:
// 해당 테스크 노드가 실행될 때 해야할 일 구현
virtual EBTNodeResult::Type ExecuteTask( UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory ) override;
// 해당 테스크 노드가 중단 되었을 때 해야할 일 구현
virtual EBTNodeResult::Type AbortTask( UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory ) override;
virtual void OnTaskFinished( UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTNodeResult::Type TaskResult ) override;
virtual void TickTask( UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds ) override;
};
// 생성자에서 노드 이름 설정
UBTTask_Main_MoveTo_Trace::UBTTask_Main_MoveTo_Trace()
{
NodeName = L"TRACE_TARGET";
}
EBTNodeResult::Type UBTTask_Main_MoveTo_Trace::ExecuteTask( UBehaviorTreeComponent & OwnerComp, uint8 * NodeMemory )
{
EBTNodeResult::Type retVal = Super::ExecuteTask( OwnerComp, NodeMemory );
return retVal;
}
EBTNodeResult::Type UBTTask_Main_MoveTo_Trace::AbortTask( UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory )
{
EBTNodeResult::Type retVal = Super::AbortTask( OwnerComp, NodeMemory );
return retVal;
}
void UBTTask_Main_MoveTo_Trace::OnTaskFinished( UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTNodeResult::Type TaskResult )
{
Super::OnTaskFinished( OwnerComp, NodeMemory, TaskResult );
}
void UBTTask_Main_MoveTo_Trace::TickTask( UBehaviorTreeComponent & OwnerComp, uint8 * NodeMemory, float DeltaSeconds )
{
Super::TickTask( OwnerComp, NodeMemory, DeltaSeconds );
}
3) BT_MonsterTree 에디터로 이동
태스크 추가 시도 시, 방금 추가한 클래스명이 Main Move to Trace로 출력된다.
(1) 이전 Moveto를 제거하고, 방금만든 트레이스를 추가하자
(2) 생성 후, 디테일창에서 블랙보드키를 Target으로 변경해주자
=> 실행하면 시야에 들어왔을 때 몬스터가 타겟 플레이어에게 잘 이동한다.
'Unreal Engine5 > KDT 2024' 카테고리의 다른 글
[UE5 KDT2024] 36. 맵 네비게이션, 팀셋팅 1편 (0) | 2025.03.20 |
---|---|
[UE5 KDT2024] 35. 비헤이비어트리, 블랙보드 (0) | 2025.03.19 |
[UE5 KDT2024] 34. AIPerception (0) | 2025.03.18 |
[UE5 KDT2024] 33. 스킬 생성 2편 (0) | 2025.03.17 |
[UE5 KDT2024] 32. 스킬 생성 1편 (0) | 2025.03.16 |
댓글