• UE AI里的感知实现流程解析


    UE AI里的感知实现流程解析

    感知与被感知流程

    刺激源的配置与注册

    1. 配置结构
    UPROPERTY(EditAnywhere, Category = "AI Perception", BlueprintReadOnly)
    TArray<TSubclassOf<UAISense> > RegisterAsSourceForSenses;
    
    • 1
    • 2

    向刺激源组件UAIPerceptionStimuliSourceComponent中添加配置

    1. 注册刺激配置到感知系统
    • 重写父类里的OnRegister函数,过滤掉RegisterAsSourceForSenses里无效的配置,根据bAutoRegisterAsSource的成员变量控制是否自注册到感知系统
    • bAutoRegisterAsSource如果为true则执行RegisterWithPerceptionSystem函数,该函数首先拿到感知系统实例指针,然后迭代配置结构中的UAISense调用感知系统中的RegisterSourceForSenseClass来完成刺激源Sense的注册及刺激源组件持有者的注册

    感知组件的配置、注册、与信息更新

    1. 感知配置项
    UPROPERTY(EditDefaultsOnly, Instanced, Category = "AI Perception")
    TArray<TObjectPtr<UAISenseConfig>> SensesConfig;
    
    • 1
    • 2

    这是感知类型配置,与刺激源配置不同的是,感知配置对UAISense进行了封装,里面添加了感知间隔的有效时间(Age),调试颜色控制,开启允许与否的控制等

    1. 注册感知配置到感知系统
    • 同样的配方,在重写的OnRegister函数里进行处理,首先在持有者AIController的OnEndPlay委托上绑定OnOwnerEndPlay函数,该函数用于在AIController玩法结束时取消所有的感知系统里的感知注册
    • 循环SensesConfig调用RegisterSenseConfig函数,该函数首先对SenseConfig进行脱壳,获取UAISense,然后调用感知系统的RegisterSenseClass函数注册UAISense配置,这个配置与刺激源注册配置是一致的
    • 添加感知组件的PerceptionFilter白名单
    • 设置刺激源留存的最大有效间隔时间(Age),这个留存时间保留在TArray<float> MaxActiveAge里,这个数组的索引与SenseID保持一致
    • 最后一步是调用感知系统的UpdateListener函数来完成在感知系统中的感知注册,该函数做了以下处理:
    1. 检查当前传入的Listener是否有效,如果有效,则说明传入的是已经注册过的Listener,那么调用FPerceptionListenerUpdateListenerProperties函数来更新该Listener的teamid及感知白名单,再调用感知系统的OnListenerUpdate函数,而该函数是迭代了已经注册的Senses配置实例,转调了Sense的OnListenerUpdate,而内部封装的是委托执行(接后续)
    2. 如果无效,则说明这是一个新的感知组件,则分配一个有效的感知ID,并向ListenerContainer添加一个新的感知实例成员,接着调用感知组件的OnNewListener函数,该函数与OnListenerUpdate函数类似,迭代注册的Senses,调用Sense的OnNewListener函数(接后续)

    感知系统中对刺激源的处理

    1. 对刺激源的注册请求处理
    • 接收到UAIPerceptionStimuliSourceComponent刺激源组件的RegisterSourceForSenseClass函数调用后,获取该刺激源的FAISenseID并检查该ID是否被实例化过,没有被实例化则调用RegisterSenseClass,接着调用RegisterSource注册该刺激Sense的持有者
    • RegisterSenseClass函数首先做的事情就是通过CDO调用UpdateSenseID更新FAISenseID分配一个有效的ID给当前Sense配置,接下来关键的一步是生成一个UAISense实例添加到感知系统的Senses结构里。然后根据UAISense里的bWantsNewPawnNotificationbAutoRegisterAllPawnsAsSources控制参数,来决定是否把世界场景下的所有Pawn都注册为该配置的刺激源Actor,如果是,则在World下一帧开启一个计时器执行刺激源Actor的添加,具体流程如下:
    1. 调用RegisterAllPawnsAsSourcesForSense函数,根据传入的SenseID迭代World下的APawn,然后调用RegisterSource函数
    2. RegisterSource函数根据迭代把每一个APawn添加为SourcesToRegister的数据成员
    • 调用RegisterSource函数,把当前持有该UAISense的作为刺激源添加到SourcesToRegister里,其实这里可以用if..else..来区分开的,不过问题不大,因为向SourcesToRegister里添加数据的时候用的是AddUnqiue函数
    1. 对感知组件的SenseConfig的处理
    • 感知组件是直接调用了RegisterSenseClass函数,该函数上面已经说过,不再赘述
    • 也就是说与刺激源不同的是感知组件没有把组件的持有者注册给感知系统,因为是感知一方,除非感知配置开启了把所有Pawn注册为刺激源的选项

    感知系统数据更新及与刺激源,感知组件三者间的数据交互

    1. 刺激源数据的更新(添加及删除刺激源数据)
    • 刺激源数据的添加:
    1. 在感知组件的Tick函数里调用PerformSourceRegistation函数,查找注册的Senses里与SourcesToRegister数据成员里关联的Sense,然后把该SourceActor通过Sense的RegisterSource函数添加到Sense感知配置实例里去,以便后续处理
    2. 绑定StimuliSourceEndPlayDelegate委托用于在SourceActor结束玩法时取消该Actor注册的刺激信息(比如下线,死亡等)
    3. 添加一个刺激源数据FPerceptionStimuliSource到RegisteredStimuliSources结构里,同时调用RelevantSensesAcceptChannel函数来设置刺激源针对于该Sense的白名单(这个白名单的用途是说明该SourceActor对该类型的感知配置有效)
    4. 重置SourcesToRegister
    • 剔除刺激源Actor的操作
    1. 这些操作来自于三方面的调用:感知组件执行Cleanup操作;刺激源组件主动调用取消注册函数;感知系统委托回调OnPerceptionStimuliSourceEndPlay
    2. 这些操作都是调用了UnregisterSource函数,该函数操作如下:
    • 查找RegisteredStimuliSources里的刺激源结构成员,然后根据传入Sense的有效性分为两种情况处理,首先是有效的情况下,说明只针对某一类型的Sense进行处理,则首先检查刺激源中的通道白名单,有效则说明该Sense里有该SourceActor,则调用Sense的UnregisterSource函数来做删除处理, 接着调用刺激源数据里的RelevantSensesFilterOutChannel函数,剔除掉SourceActor对该Sense配置的响应(拉入黑名单)
    • 如果无效则需要考虑迭代所有的Sense了,然后根据与指定Sense的剔除操作相同,首先检查刺激源白名单,然后剔除,最后调用刺激源RelevantSensesClear函数清空对所有Sense的响应
    • 如果刺激源的RelevanttSenses是被清空了,则说明是该SourceActor已经对感知系统无效了,则从RegisteredStimuliSoruces里移除,同时在SourceActor的OnEndPlay里取消StimuliSourceEndPlayDelegate的委托注册,因为SourceActor主动撤销了,而不需要等到OnEndPlay
    • SourceToRegister待处理的刺激源数据里移除掉关于该SourceActor的注册数据
    • 感知Listener的更新
    1. 之前在注册感知配置的时候是一种添加方式
    2. 还有一种方式是请求更新,主要是调用RequestStimuliListenerUpdate函数,而该函数主要调用了 UpdateListener函数
    3. 感知系统中的OnListenerConfigUpdated函数用于处理感知组件中指定SenseConfig变更时的操作,主要调用已注册的Sense里的配置更新回调
    • 感知的移除
    1. 感知的移除主要来自于两个方面,一个是感知组件执行清除操作时对感知系统UnregisterSource的调用;另一个是来自于感知系统Tick函数里来自于Listener的有效性检查
    2. UnregisterSource函数迭代了注册的Senses,Listener中是否包含该Sense的判断来调用Sense里的OnListenerRemoved函数
    • 刺激源的到期处理
    1. 理解这个问题之前首先要搞清楚的几个感知系统里的成员变量:
    • PerceptionAgingRate 该属性决定了多久执行一次感知系统对刺激源信息的处理,默认为0.3f秒,可配置
    • NextStimuliAgingTick 该属性标示了下一次要执行刺激源留存时间计算的时间,由当前系统时间加上PerceptionAgingRate得出
    • PerceptionAgingRate + (CurrentTime - NextStimuliAgingTick) 不难看出这个是用来计算出上一次执行到现在真正过去的时间,这个计算结果用于处理刺激源信息里Age计算
    1. 在感知系统的Tick函数里,通过NextStimuliAgingTick检查来决定是否对刺激源信息进行处理,在处理的时候调用感知系统里的AgeStimuli,该函数主要是迭代注册的ListenerContainer,而最终会调用感知组件里的AgeStimuli这个函数用来检索感知组件上的感知数据PerceptualData里的每一个感知数据,然后迭代次信息上一次获取的刺激源信息LastSensedStimuli,计算Age加上当前距上一次计算过去的时间,看是否达到间隙留存时间上限,如果已达到留存时间则则给该刺激源信息打上过期标签,并调用RegisterStimulus函数添加到StimulusToProcess结构里,该结构在ProcessStimuli函数处理数据时调用,最终需要说明的是当AgeStimuli返回的结果为true时,会给FPerceptionListener打上刺激源待处理标签,在处理刺激源消息时会检查该标签以此决定是否要处理刺激消息更新
    2. 那么有哪些情况会更新处理刺激源信息哪?
    • 在感知系统的Tick里处理的了三种情况:1. 刺激源信息里有数据达到留存时间过期标识;2. Senses里有Sense到期Tick;3. 有Listener处于过期状态需要处理
    • 当有以上三种情况发生时,会调用感知组件的ProcessStimuli函数进行刺激更新数据处理
  • 相关阅读:
    NeuroImage:通信辅助技术削弱了脑间同步?看来维系情感还得面对面互动才行...
    c#采用toml做配置文件的坑过
    互换性与技术测量试题及答案(4套)
    树查找(暑假每日一题 18)
    RPA案例|云扩助力保险行业开启超自动化运营新阶段
    Python函数详解(二)——函数的参数传递基础
    【sql】笔记大屏展示数据查询
    【PTE-day06 文件上传】
    STARK Low Degree Testing——FRI
    如何在SAP GUI中快速执行新的事务代码
  • 原文地址:https://blog.csdn.net/u011047958/article/details/125598813