AI之所以只感知Pawn,是因为被感知的对象需要手动注册,而非全局搜索。
在BeginPlay阶段,为所有的AISense,调用RegisterAllPawnsAsSourcesForSense
UWorld::BeginPlay()
GetAISystem()->StartPlay();
void UAISystem::StartPlay()
PerceptionSystem->StartPlay();
void UAIPerceptionSystem::StartPlay()
for (UAISense* Sense : Senses)
{
if (Sense != nullptr && Sense->ShouldAutoRegisterAllPawnsAsSources())
{
FAISenseID SenseID = Sense->GetSenseID();
RegisterAllPawnsAsSourcesForSense(SenseID);
}
}
并且之后再通过RegisterSenseClass新注册的AISense,也会调用RegisterAllPawnsAsSourcesForSense
FAISenseID UAIPerceptionSystem::RegisterSenseClass(TSubclassOf<UAISense> SenseClass)
FAISenseID SenseID = UAISense::GetSenseID(SenseClass);
if (Senses[SenseID] == nullptr)
Senses[SenseID] = NewObject<UAISense>(this, SenseClass);
if (Senses[SenseID]->ShouldAutoRegisterAllPawnsAsSources())
World->GetTimerManager().SetTimerForNextTick(FTimerDelegate::CreateUObject(this, &UAIPerceptionSystem::RegisterAllPawnsAsSourcesForSense, SenseID));
RegisterAllPawnsAsSourcesForSense则是检索所有的Pawn进行注册
for (TActorIterator<APawn> PawnIt(World); PawnIt; ++PawnIt)
{
RegisterSource(SenseID, **PawnIt);
}
在UAISystem::PostInitProperties()内,注册了Actor生成的委托
if (WorldOuter)
{
FOnActorSpawned::FDelegate ActorSpawnedDelegate = FOnActorSpawned::FDelegate::CreateUObject(this, &UAISystem::OnActorSpawned);
ActorSpawnedDelegateHandle = WorldOuter->AddOnActorSpawnedHandler(ActorSpawnedDelegate);
}
而在void UAISystem::OnActorSpawned(AActor* SpawnedActor)内,就判断如果生成的是Pawn,感知系统会进行添加
APawn* AsPawn = Cast<APawn>(SpawnedActor);
if (AsPawn)
{
PerceptionSystem->OnNewPawn(*AsPawn);
}
在void UAIPerceptionSystem::OnNewPawn(APawn& Pawn)内,对于每个AISense,如果bAutoRegisterAllPawnsAsSources为true,则进行注册
for (UAISense* Sense : Senses)
{
if (Sense->ShouldAutoRegisterAllPawnsAsSources())
{
FAISenseID SenseID = Sense->GetSenseID();
RegisterSource(SenseID, Pawn);
}
}
void UAIPerceptionSystem::RegisterSource(FAISenseID SenseID, AActor& SourceActor)则是先把要注册的信息添加到SourcesToRegister内
SourcesToRegister.AddUnique(FPerceptionSourceRegistration(SenseID, &SourceActor));
之后在Tick时进行处理
UAIPerceptionSystem::PerformSourceRegistration()
Senses[PercSource.SenseID]->RegisterSource(*SourceActor);
以Sight为例
void UAISense_Sight::RegisterSource(AActor& SourceActor)
bool UAISense_Sight::RegisterTarget(AActor& TargetActor, const TFunction<void(FAISightQuery&)>& OnAddedFunc /*= nullptr*/)
通过FAISenseAffiliationFilter::ShouldSenseTeam判断是否是需要检测的目标,注意若没有继承IGenericTeamAgentInterface,则视为中立方,需要勾选Sense Config的DetectNeutrals
综上所述,我们只需要对已经生成的对象和新生成的对象,对于UAIPerceptionSystem的所有Sense,调用RegisterSource即可。
RegisterSource有一个是内部版本,参数是RegisterSource(FAISenseID SenseID, AActor& SourceActor),这个是内部使用的。我们只需要调用包装好的版本:
template<typename FSenseClass>
void RegisterSource(AActor& SourceActor);
/** Registers given actor as a source for all registered senses */
void RegisterSource(AActor& SourceActor);
void RegisterSourceForSenseClass(TSubclassOf<UAISense> Sense, AActor& Target);
并且注册后就不用管了,因为已经在EndPlay时绑定了UnregisterSource函数
void UAIPerceptionSystem::PerformSourceRegistration()
for (const FPerceptionSourceRegistration& PercSource : SourcesToRegister)
SourceActor->OnEndPlay.AddUnique(StimuliSourceEndPlayDelegate);
StimuliSourceEndPlayDelegate.BindDynamic(this, &UAIPerceptionSystem::OnPerceptionStimuliSourceEndPlay);
void UAIPerceptionSystem::OnPerceptionStimuliSourceEndPlay(AActor* Actor, EEndPlayReason::Type EndPlayReason)
{
UnregisterSource(*Actor);
}