Unity使用Behavior Tree的小小心得

[使用Behavior的注意事项]
1、需要确定支持的版本,一定要使用全新版本,可以去游戏蛮牛获取。
2、导入Behavior Designer插件包后,进入Unity->Tools->Behavior Designer->Edior.
3、如果机子很差,使用起来会有很大的延迟,特别是Eidor选中Tasks选项的时候,为了防止延迟现象,最好把Tasks下的全部选项都收起来,这样才不会很卡。


下面正式介绍:
Behavior 行为树:


一、Composities组合任务
组合任务必须要有子任务,即它由子任务组成。
1.Sequence是序列任务,它的子任务必须全部依次执行成功(自左往右),返回成功,当中间有一个任务执行失败,后面的任务都不会执行了,返回失败。行为树执行任务只会一个一个地去执行,当一个任务没有被


执行完(返回失败或者成功,都算执行完),不会再去调用另外一个任务的。(Sequence返回结果相当于And、与 运算)


2.Selector是或任务,或序列任务的子任务只要有一个执行成功,那么Selector就返回成功,行为树就不会再去执行Selector下其他的任务了,只有当有一个Selector子任务失败了,那么Selector后面的那个任务才会


被执行,一直到执行成功为止,也就是说Selector序列任务下的子任务只能有一个任务被成功执行,如果全部子任务都失败了,Selector返回失败。(Selector是或任务,即or操作)


(Sequence和Selector的Inspector的Abort Type:Self、Lower Priority、Both类型解释)
当选择了这3个其中一个的时候,序列任务And、Or都会因为它的Conditions子任务的状态发生第二次变化,而终止一个任务。
Self是终止自身,Lower Priority是终止比它要低级的任务,当然是在同个任务层的,左->右,任务优先级高->低,Both是即终止自身也终止比它优先级要低的任务。
注意:选择Self后,如果这个序列任务完成了(成功或者失败),那么将不会再检测这个序列任务下的子任务状态变化,而产生中断自身,只有这个序列任务是一直都在运行的时候,它才会检测它的子任务状态,根据


子任务状态发生变化而终止自身。
选择Lower Priority后,如果这个序列任务正在执行,那么将不会检测它身下的子任务状态变化,而只有当该序列任务结束后,开始执行下一个任务(肯定是比它低级的任务),这时才会去检测它的子任务状态,当它


的子任务状态改变后,会中断这个低级任务,转移到该序列任务继续执行。
可以直接看Edior下的图来观察是在检测哪个任务的状态,被检测的任务中的图标是会有圆圈的,而没有被检测的任务是没有圆圈的


例如:Sequence任务序列下有一个子任务失败了,那么Sequence任务会返回失败,开始执行别的任务,如果在某一时刻,Sequence下的那个失败的任务状态发生改变,即成功了,若Type是Self会结束Sequence任


务,这没有意义,但是如果Type是Lower Priority,那么会终止比它要低级的任务,再次开始执行Sequence下的子任务!(牛逼。。)


二、Actions:
1.Idle是返回任务running状态,行为树执行到该Idle行为的时候会停在Idle处,除非用户去中断Idle的运行。
2.Log是Debug.Log()行为,Comment是Log在Edior的提示信息,Text是这个Log的内容。


导入Behavior Designer - Movement_Pack_v1.3.2
Actions下的Movement:
1.Patrol巡逻任务,Speed速度,Angular Speed角速度,Arrive Distance临界距离,wayponts巡逻点,临界距离是指这个物体与巡逻点之间的距离,如果少于临界距离表示该物体已经到达巡逻点,可以向下一个


巡逻点移动了。前提条件:该物体必须挂载一个NavMeshAgent组件,它的巡逻移动是依靠NavMesh来移动的。
2.Seek寻找人物,与Patrol不同点是有一个寻找位置设置,当到达寻找位置后,就返回成功,若没到达寻找的位置就一直运行追踪寻.找位置.


三、Conditionals
Movement:
1.Can See Object:发现物体任务,可指定Target Object目标物体,当发现了就会返回true,第二种指定方法是指定Object Layer Mask指定一个层,当发现指定层Mask的物体的时候返回true,View Distance是视野


距离,Object In Sight是视野内的物体transform传递给指定变量。


四、Decorators
1.Inverter行为:将子任务结果取反


Variables变量面板:
变量可设置给任务属性(属性类型和变量类型一定要一致!),任务也可以向变量传递数据。
局部变量:一个行为树的局部变量,只能够在它自身下访问到。(类似于键值对变量)
全局变量:一个行为树的全局变量,能够被全部行为树的行为访问到。(类似于键值对变量,但是设置值和取值操作都类似于PlayerPres)。一个全局变量的设置和获取都是:行为树脚本变量.SetVariable("键


名",value);以及.GetVariable("键名"):注意:全局变量的范围是在一个工程的全部行为树有效,行为树脚本变量是指声明全局变量的行为树,其他行为树若要使用该行为树声明的全局变量,需要通过 


behaviorTree.GetVariable("键名")来获取,全局变量存的是SharedVariable类型的键值对! 上面SetVariable的value类型是SharedVariable记住了!!(behaviorTree)是可以通过GetComponent<BehaviorTree>


()来获取的
1.如果一个物体身上有多个行为树脚本,即BehaviorTree有多个,可以通过Group 或者 BehaviorName来区分!最好在一个物体身上创建一个TreeManager行为树管理脚本来管理这些行为树的启动和禁用。
2.行为树内的自定义方法 即继承了Action、 Conditional、 Decorator、 Composities类的其中一个的,都算是自定义行为树方法,在这些方法内我们依然可以通过GetComponent来获取当前行为树所在的物体身


上的组件。也就是说很方便地可以与非行为树脚本进行交互。
3.最最最重要的来了,如果你想要把一个拥有行为树的物体制作成预制体,你必须要考虑好你行为树所使用的所有变量都必须是来源于预制体或者是实时获取的!来源于预制体的变量基本是单一不变的,来源于实时的


就是可变的,打个比方:我要使用Action-Animation的Play,那么你可指定一个预制体过去,那个预制体保存有动画,这些动画肯定是不可变的,单一的。而如果你想要弄敌人搜索附近玩家,那么这样肯定是需要实


时地获取玩家了,这样你就必须要通过代码来获取了。为什么不可以将非预制体的给行为树变量呢?因为一旦成为预制体,所有非预制体的变量内容都会清空,这是常识,可是总有人会中招啊!老是发现弄成预制体后


,一堆空指针,还在那里各种排查丢失问题,其实是上面我所说的情况。


一个局部变量就简单了直接写(字段名.Value)设置和取值。
这些变量都是共享变量,需要引用using BehaviorDesigner.Runtime; 全都是SharedXxxxxx开头的变量类型.
好处:使用Behavior Designer下的共享变量方便我们统一修改全部使用相同变量的行为属性值,而且还能够让行为互相传递数据,让行为更加灵动。


行为树的生命周期函数:
任务生命周期函数:
OnAwake(),任务被激活的时候执行,只会执行一次
OnStart(),任务开始的时候执行,可执行多次
OnUpdate(),任务更新的时候执行,每帧执行直至任务返回成功或者失败
OnEnd(),任务结束的时候执行,可执行多次
行为树的函数:
OnBehaviorComplete(),当整个行为树的任务都完成了之后会执行一次。 
在行为树里面可以使用Debug.Log()来进行检测,无法用print()

猜你喜欢

转载自blog.csdn.net/qq_39574690/article/details/80219620
今日推荐