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<FAINoiseEvent> NoiseEvents
,UAISense_Damage
里的TArray<FAIDamageEvent> 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<FAITouchEvent> RegisteredEvents
存放来自于感知系统的触觉事件- 重写的
Update
函数中对缓存的事件进行迭代,并查找触觉Sense里的Listener,调用RegisterStimulus
函数发送刺激源消息给指定的Listener
,然后清空事件数组,完成整个事件流程
UAISenseConfig_Blueprint
和UAISense_Blueprint
UAISenseConfig_Blueprint
仅包含配置UAISense_Blueprint
的属性及获取接口UAISense_Blueprint
为蓝图实现一种Sense提供的范本基类,事实上通过看这个类的实现函数就可以知道,定义一个自身的需要的Sense类需要那些内容:OnUpdate
,OnListenerUpdated
,OnListenerRegistered
,OnListenerUnregistered
,K2_OnNewPawn
UAISense_Blueprint
整体上对UAISense
必须要处理的问题的框架封装,然后转交给蓝图实现
FAITeamStimulusEvent
和UAISense_Team
UAISense_Team
是一个用来向其他同阵营成员通知刺激消息的Sense,本身功能非常简单,可以用来转发该AI接受到的刺激消息,比如受到攻击,听到有敌对成员靠近,进而告诉同阵营成员做出策略反应UAISense_Team
本身逻辑非常简单,添加了一个维护自身消息的结构数组,同时实现了接受事件的函数RegisterEvent
以及维持消息循环迭代发送刺激消息的函数Update
,Update
函数里循环迭代了Listener以及RegisteredEvents
FAITeamStimulusEvent
里定义了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 噪声源Actor Tag 事件附加标签 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<FAISightQuery> SightQueriesOutOfRange
和TArray<FAISightQuery> 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注册与取消实现都不是必须的,它取决于你的实现需求