设计的本质是抽象变化。
关于Composites的用法又有了新的认识:在Task返回值的时候尽量要与真实情况相符合,就像Selector下的子节点一旦成功Selector就返回,说明Selector是一种选择的方式,而Sequence下的子节点一旦失败就返回,说明它就是用来一步一步执行的。所以在选择使用的时候尽量按照规矩来,减少强制性的返回结果,这样显得更和谐,别人读程序也更好懂。
上面是整个AI系统中的工具。
Pawn Action
也是要在AIController中运行的,有两个特点,Action Tree是动态创建的,2是执行结束后就被释放了。
用处:将一些行为封装成起来可以供很多不同的目标使用。
使用方法:创建一个基于PawnAction_BlueprientBase的类,在其中有5个事件驱动,常用的就是EventActionStart/Tick/Finished,如何判定是要执行Finish呢,有一个节点使用:Finish,并且还可以返回Result。那现在类是有了,将一些AI事件放在这里写,如何去实现呢,这个类只能在有特定的Compontent的下执行,是ActionsComp,这个组件存在于AIController中,所以类的实现在其中写,首先要创建一个Action,用节点CreatActionInstance,创建出来只是将其放在栈中,还需要将其从栈中调出,至于何时调出根据需求,调出有两个节点可以使用,PushAction和PerformAction。当然最后要将AICON帮到目标身上才能触发。
当然,还有abortAction打断节点。
SaveGame
将游戏记录存下来应该是在游戏进行的最高逻辑中写,GameInstance。
在GameInstance中有一个Init初始化事件,这是最适合写存档的地方。
创建一个基于SaveGame的类,这个类的主要作用是存储数据。所以不需要写任何逻辑,只需要将需要存储的变量添加上就Ok。等待别的地方对变量赋值。
接下来在GameInstance中写逻辑,复写一个Event Init的函数,链接LoadGameformSlot节点,意思是从Slot中载入游戏,Solt中放有什么呢,我们要把游戏的档案放在Slot中,这样就会一开始的时候从Slot中调用档案了,节点的返回值就是游戏的档案,这里的游戏档案就是SaveGame的类,这里做一个类型转换,如果第一次进入游戏,这是还没有存过档,那么一定会转换失败,失败之后就要创建一个档案了,CreatSaveGameObject,创建一个存储游戏的对象,并将其付给刚创建的类的引用,使其实例化。如果之前想打开之前有过的存档,那么就会转化成功,成功之后将打开的游戏档案再付给引用,将其实例化。这是进入和创建游戏档案的逻辑。
下面要写的是如何将数据存在游戏档案中,更新数据一定要对对象更新,不能对引用更新,所以在Instance中的引用是被实例化了的,故可以对其更新数据,进行数据赋值,赋值形式根据需求自定,这里我只做了一个很简单的赋值。
或许很疑惑这里为什么还有一个Instance中的函数,理论上说我们已经完成了将数据存入档案,一开始调用档案的事情了。但是别忘了,调用档案,是从Slot中调用的,所以我们还必须要将档案在存储进Slot中的,否则调用的还是上一次放在Slot中的档案。那既然是要把改变好的档案放在Slot中,那还是要在Instance中放的,因为只有Instance中有被实例化的档案引用。但是存储的驱动有不能再Instance中,故写成函数,由其他地方调用。这样才完成了游戏存档。
梳理一下思路,基于SaveGame的类就是一本游戏档案,其中只有变量。类是创建好了,那么下面要创建出对象,CreatSaveGameObject,那这个创建的逻辑要在Instance中写了,并将创建好的提升为变量,供其它使用。UE的机制是不能直接打开存档,而是要从Slot中打开档案,要想打开档案就要用LoadGameformSlot,要是第一次开游戏的话需要创建,第二次就直接打开已有的存档就OK了,即使是打开了,也需要将打开的存档提升为变量,以备更新信息时用。故有了Instance中的逻辑。那么既然要从Slot中打开游戏档案,那么我们在将数据存档的时候就要将档案放在Slot中,那么最后就是更新数据了,这里很简单的信息交互就不说了。
Sensing
Register登记,注册 perception感知 stimuli刺激
这里存在两个感知系统,其中AIPerception是老的感知系统,也是比较完善的,虽然会被淘汰,但那时还是需要了解的,新的系统是PawnSensing。先说AIPerpection.
AIPerception
首先在AIController中添加一个AIPerception,并在Detail面板中找到AIPerception对其进行编辑,Configuration布局,结构。Dominant主要的,显性的。
添加一个elements,例如添加一个Hearing,对Sense面板进行修改达到自己要的效果,Implementation工具,Affiliation加入,入会,联系Detevtion by Affiliation探测的目标——敌人,中立,朋友。MaxAge听完之后多久忘掉,还有一个LoSHearingRange多远听不到了
如果是视觉的话还有一个视觉角度。
设置好之后会发现在Variables中的Components有一个AIPerception,在Detail面板中有事件可以进行编辑,
一般用到前两个,当感知更新时执行事件,那这个检测的任务就交给了引擎,较于之前的方法,我们就不需要进行监测了,那执行的事件很明了就是要更新黑板上的数据。这里的两个驱动有不同的地方,就是没有Target的驱动是可以对多个AI,后者只是针对一个。
事件执行后,我们仍要进行判定,判断是一旦受到刺激就执行事件,还是守到某种特定的刺激执行特定的事件。这里用到节点GetSenseClassForStimulus(获得产生刺激的感知类)之后跟判定ClassIsChildOf。
对Array的遍历主要是想获得Srimulus(刺激)进行判定。先是获得元素的Perception,接着将其Break获得Stimulus数组,再进行遍历,将元素Break进行判定。
第二种就是对其单个感知的改变进行判定,获得发生刺激的感知类,之后判断是不是继承自需要的感知类。
如此结束了么,这里有这么个问题就是这个感知系统的弊端,AIPerception负责接收刺激,还必须有人发出刺激,这就是AIPerceptionStimuliSource组件的作用。在目标中添加此组件,在Detail面板中的AIPercetion勾选Auto Register as Source才可以产生将刺激发出的作用,并在Register as Source for Senses中添加需要发出的刺激。还有一点需要注意,对于听觉而言,动画中的声音对于场景中的Actor是听不到的,所以即使发出了听觉刺激还是没有声音的,这里就要让场景中的Actor也可以拥有一个声音的刺激。利用节点MakeNoise,和PawnMakeNoise。两者有一点不同,NoiseMaker(声音制造者)NoiseInstigator(声音煽动者)。
制作一个声音刺激,什么时候发出呢,当然是要跟动画声音的产生同步的,在动画中创建一个Notify。如此才算完成。
Pwan Sensing
在前面阐述过。