• UE AIPerception感知非Pawn对象


    原理部分

    AI之所以只感知Pawn,是因为被感知的对象需要手动注册,而非全局搜索。

    检索已存在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);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    并且之后再通过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));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    RegisterAllPawnsAsSourcesForSense则是检索所有的Pawn进行注册

    for (TActorIterator<APawn> PawnIt(World); PawnIt; ++PawnIt)
    {
    	RegisterSource(SenseID, **PawnIt);
    }
    
    • 1
    • 2
    • 3
    • 4

    处理新生成的Pawn

    在UAISystem::PostInitProperties()内,注册了Actor生成的委托

    if (WorldOuter)
    {
    	FOnActorSpawned::FDelegate ActorSpawnedDelegate = FOnActorSpawned::FDelegate::CreateUObject(this, &UAISystem::OnActorSpawned);
    	ActorSpawnedDelegateHandle = WorldOuter->AddOnActorSpawnedHandler(ActorSpawnedDelegate);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    而在void UAISystem::OnActorSpawned(AActor* SpawnedActor)内,就判断如果生成的是Pawn,感知系统会进行添加

    APawn* AsPawn = Cast<APawn>(SpawnedActor);
    if (AsPawn)
    {
    	PerceptionSystem->OnNewPawn(*AsPawn);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在void UAIPerceptionSystem::OnNewPawn(APawn& Pawn)内,对于每个AISense,如果bAutoRegisterAllPawnsAsSources为true,则进行注册

    for (UAISense* Sense : Senses)
    {
    	if (Sense->ShouldAutoRegisterAllPawnsAsSources())
    	{
    		FAISenseID SenseID = Sense->GetSenseID();
    		RegisterSource(SenseID, Pawn);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    注册Pawn

    void UAIPerceptionSystem::RegisterSource(FAISenseID SenseID, AActor& SourceActor)则是先把要注册的信息添加到SourcesToRegister内

    SourcesToRegister.AddUnique(FPerceptionSourceRegistration(SenseID, &SourceActor));
    
    • 1

    之后在Tick时进行处理

    UAIPerceptionSystem::PerformSourceRegistration()
    	Senses[PercSource.SenseID]->RegisterSource(*SourceActor);
    
    • 1
    • 2

    以Sight为例

    void UAISense_Sight::RegisterSource(AActor& SourceActor)
    bool UAISense_Sight::RegisterTarget(AActor& TargetActor, const TFunction<void(FAISightQuery&)>& OnAddedFunc /*= nullptr*/)
    
    • 1
    • 2

    通过FAISenseAffiliationFilter::ShouldSenseTeam判断是否是需要检测的目标,注意若没有继承IGenericTeamAgentInterface,则视为中立方,需要勾选Sense Config的DetectNeutrals

    感知非Pawn对象

    综上所述,我们只需要对已经生成的对象和新生成的对象,对于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);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    并且注册后就不用管了,因为已经在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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    C语言 | Leetcode C语言题解之第187题重复的DNA序列
    手机照片备份方案Immich
    Powerdesigner支持的数据库系统
    qmake 手册:创建项目文件
    ABAP ALV 删除按钮标准写法
    完美结合,10款提升编程能力的游戏项目
    关于计算机找不到d3dx9_43.dll,无法继续执行代码修复方法
    9.OpenFeign服务接口调用
    win11打开方式没有始终,例如pptx应该用power point打开
    Mybatis快速上手
  • 原文地址:https://blog.csdn.net/jk_chen_acmer/article/details/128186952