UAISense的辅助结构
UAISenseEvent事件结构
UAISenseEvent本身更多的是充当类似于接口的功能,最关键的函数定义是GetSenseID,这个函数交由继承的子类负责实现,而子类会通过UAISense::GetSenseID(UAISense_XXX)获取全局唯一的实例SenseID,GetSenseID函数最终会调用GetDefaultObject里的SenseID,GetSenseID获取到的ID可以作为获取实例的索引,在感知系统中,通过传入的UAISenseEvent获取ID,再遍历Sense调用指定Sense的事件注册函数,转到Sense中时,会尝试转换为自身定义的事件类型,添加到事件结构里,等待Update循环遍历处理从整个执行的流程来看UAISenseEvent主要为了完成向UAISense投递数据,无论是从Sense本身,还是从感知系统,最终会把事件投递到Sense里的事件数组里,比如UAISense_Hearing的TArray NoiseEvents,UAISense_Damage里的TArray RegisteredEvents感知系统里维护了三个事件相关的模板函数OnEvent``OnEventBatch``static OnEvent以及两个蓝图可调用的两个函数ReportEvent、static ReportPerceptionEvent,这些函数最终会索引到指定的Sense实例,转调Sense里的注册事件函数,与感知系统相对应,Sense类定义的同义的函数。如果有注册事件的需求,那就可以实现UAISense基类里的RegisterWrappedEvent或者对应感知系统里的事件函数,接收事件并处理即可
UAISenseConfig辅助配置结构
UAISenseConfig本身为UAISense提供了调试颜色,过期遗忘控制变量,开始时启动,感知名缓存,获取感知ID方法等继承自UAISenseConfig的子类充当该Listener感知Sense的参数配置,通过这些配置完成一个实例Listener的定制化需求比较典型的配置是UAISenseConfig_Sight,视觉配置的参数在AIHelpers.h里的CheckIsTargetInSightCone函数调用中使用,而该函数正是在UAISenseConfig_Sight的Update函数中被调用
UAISense各种Sense具体实现
UAISense里为继承的子类完成了哪些内容并且不需要子类实现?
基类实现关于Tick更新的机制,这些机制依赖于感知系统的Tick函数,并通过自身的ProgressTime函数处理计时问题实现了Listener变动处理的函数调用框架,这些Listener的函数都是被感知系统调用,进而把Listener的变动通知给具体的Sense实例同样,实现了操作刺激源Actor的函数框架及调用逻辑实现了对Sense操作的辅助函数,这些函数可以直接被使用并不需要被重写操作定义了变动通知的控制变量及函数获取方法
UAISense的那些东西需要子类实现?
与框架定义相对应,子类要完成自身的Update函数功能,来持续的向Listener刷新数据需要完成具体的事件处理函数,无论时存储到自身的临时缓存结构里还是其他的一些处理,你会发现这些事件处理函数都很有用,当然,这些事件处理函数不是必须的,根据自身定义的Sense需求酌情处理需要处理来自于感知系统的Listener和Source数据,这些可参考其他的Sense实例参考UAISense的虚函数及感知系统对Sense的各种调用,或者已经实现的感知Sense都能达到目的
UAISenseConfig_Touch和UAISense_Touch
FAITouchEvent很简单,定义了三个参数:触觉接受的对象Listener,触觉来源Actor,触觉传来的位置坐标UAISense_Touch本身也很简单顶一个了一个RegisterEvent函数,被感知系统调用,定义了一个时间缓存数组TArray RegisteredEvents存放来自于感知系统的触觉事件重写的Update函数中对缓存的事件进行迭代,并查找触觉Sense里的Listener,调用RegisterStimulus函数发送刺激源消息给指定的Listener,然后清空事件数组,完成整个事件流程
UAISenseConfig_Blueprint和UAISense_Blueprint
UAISenseConfig_Blueprint仅包含配置UAISense_Blueprint的属性及获取接口UAISense_Blueprint为蓝图实现一种Sense提供的范本基类,事实上通过看这个类的实现函数就可以知道,定义一个自身的需要的Sense类需要那些内容:OnUpdate,OnListenerUpdated,OnListenerRegistered,OnListenerUnregistered,K2_OnNewPawnUAISense_Blueprint整体上对UAISense必须要处理的问题的框架封装,然后转交给蓝图实现
FAITeamStimulusEvent和UAISense_Team
UAISense_Team是一个用来向其他同阵营成员通知刺激消息的Sense,本身功能非常简单,可以用来转发该AI接受到的刺激消息,比如受到攻击,听到有敌对成员靠近,进而告诉同阵营成员做出策略反应UAISense_Team本身逻辑非常简单,添加了一个维护自身消息的结构数组,同时实现了接受事件的函数RegisterEvent以及维持消息循环迭代发送刺激消息的函数Update,Update函数里循环迭代了Listener以及RegisteredEventsFAITeamStimulusEvent里定义了TeamIdentifier,被通知的对象,要通知的成员,成员的缓存位置,有效通知范围用于校验是否被通知
UAISense_Prediction及FAIPredictionEvent
UAISense_Prediction预测一个Actor指定时间段内的位置,这依赖于这个Actor实体的速度及Event中传递的TimeToPredict时间值,UAISense_Prediction本身代码非常简单,提取FAIPredictionEvent里的被预测对象计算时间位置并向请求对象发送刺激源消息
UAISense_Damage和FAIDamageEvent
FAIDamageEvent:属性内容:伤害量,位置,击中位置,伤害来源(Actor),Tag标签UAISense_Damage本身实现了RegisterEvent``RegisterWrappedEvent``ReportDamageEvent函数,用于注册消息,而更新函数里只是遍历查询,把刺激消息投递掉指定的Listener里
UAISense_Hearing,FAINoiseEvent,UAISenseConfig_Hearing
UAISenseConfig_Hearing里的配置参数: 属性名定义用途HearingRange听力范围LoSHearingRange声音丢失范围bUseLoSHearing是否使用声源丢失参数,有运行时消耗提示DetectionByAffiliation检测过滤,敌对关系过滤 FAINoiseEvent参数配置 属性名定义用途Age过期标识变量NoiseLocation噪声源产生位置Loudness声源系数,作用到HearingRangeSq,进而影响听力范围MaxRange最大的听力范围Instigator噪声源ActorTag事件附加标签TeamIdentifier团队阵营归属 UAISense_Hearing里也维护了事件缓存数组结构,在Update里进行迭代处理并重置,声音感知排除一些系数和衰减控制外,最核心的计算是感知位置与噪声位置的距离和配置里设置的听力范围的比较,在听力范围内的,会向Listener发送一个延迟刺激源消息
UAISense_Sight和UAISenseConfig_Sight
UAISenseConfig_Sight中的配置设置 属性用途SightRadius可视化半径LoseSightRadius失焦半径PeripheralVisionAngleDegress外围可视角度,用于判定是否超过两侧视域DetectionByAffiliation同盟阵营检测PointOfViewBackwardOffset视锥体后移距离NearClippingRadius近裁剪视锥体可见半径AutoSuccessRangeFromLastSeenLocation上一次看到的位置是否自动返回可视结果 FAISightEvent事件属性 属性用途EventType事件类型是收到可视状态,还是丢失可视状态SeenActor被看到的对象Observer观察者对象Age留存时间 UAISense_Sight的实现
在AIHelpers文件里有一个函数CheckIsTargetInSightCone,该函数用来判断一个Actor是否处于观察者的视野内,而UAISense_Sight里的辅助结构FDigestedSightProperties正是为了辅助完成视野计算而存在的知道了视觉判定,接下来需要知道的是,为了减少视觉循环迭代的性能消耗,把可视查询分为了两部分内容,TArray SightQueriesOutOfRange和TArray SightQueriesInRange,超出视觉范围和视觉范围内两部分,每次查询结构时,都是经过排序的有序数据结构结构,因此通过NextOutOfRangeIndex索引游标很轻松找到下一个超出范围的成员索引可视化Sense要比其他的几个实现复杂,性能消耗也较高,如果对复杂的可视Sense不感冒,可以自己整一个简化版
一些关于写自定义Sense的注意事项
如果你使用关于感知系统里的事件注册模板函数,那么一些定义必不可少,比如事件结构里的typedef class UAISense_XXX FSenseClass;缺了它,OnEvent的模板函数将无法识别你事件结构类型如果Update的返回值为SuspendNextUpdate,那意味着你需要手动添加事件驱动Sense更新,而且必须调用RequestImmediateUpdate函数使得刺激源消息得到更新发送怎样执行Update的更新策略这取决于你的需求,一般情况下Sense不会主动发起更新的,比如说Damage,Touch,Hearing这些都是惰性Sense,需要主动发起事件请求才会奏效,而这最核心的函数调用是RequestImmediateUpdate这会使Sense立即刷新,如果满足刺激源消息发送,则会向指定的Listener发送刺激源消息StimulusStrength不要小于0,这会影响刺激源消息的激活状态判定及Age的过期计算,进而影响到刺激源消息的更新Sense的事件,Listener的注册与更新,Source注册与取消实现都不是必须的,它取决于你的实现需求