Unity Behavior Designer插件学习笔记与工作总结
为什么要用Behavior Designer
日常工作中,我们总要用代码实现各种各样的AI,例如怪物攻击玩家角色,通常情况下有三个阶段,Idle(原地挂机,通常与玩家离的较远的情况),WalkOrRun(与玩家到一定距离,开始追寻玩家),Attack(到达一定距离开始攻击玩家)。
通常情况下我们会使用状态机去实现上述功能,但是当情况较多,实现起来较为复杂,因此我们可以使用Behavior Designer插件,由这个插件带你去控制。说白了就是别人封装好了一个脚本,你只要填入相对应的参数,就可以实现状态机的控制效果。
实现一个最简单的Behavior Designer
如上图所示,选中一个Cube,然后再Tool打开Behavior Designer,在行为树面板中右键添加行为树,然后增加一个Action(行为,具体做什么)Log,运行即可看到输出了我们在Inspector面板中的信息。
Behavior Designer的属性介绍
介绍一下面板中的属性:
Behavior
Behavior:该行为树的一些设置,根据自己项目中需要进行设置。
Tasks
Composites:复合使用,代表当前节点有多个行为复合,常用的是Selector(实现Or的效果,多个行为节点中只使用其中一个),Sequence(实现队列效果,挨个挨个执行,如果其中有行为返回了false,那么则直接结束)。
目前我常用的是上述两个,后续有案例详细讲解。
Decorators:装饰节点,可以对单个节点进行装饰,例如翻转,希望获取是否的效果,那么则继续进行。Inverter。没怎么用过,此处暂不介绍,后续如果用到会进一步补充。
Actions:行为节点,即具体干什么内容,例如Log。
Conditionals:判断节点,判断是否满足条件,可以与Sequence配合起来使用,如果满足条件则执行后续内容,不满足条件则直接退出当前状态。
Variables
变量值,此处可以添加当前行为树下一些共用的值(引用类型),后面会详细讲解。
Inspector
属性设置,和unity自带的一样,此处不再过多讲解。
案例1:实现最简单的怪物巡逻效果
此处我们假设Cube为敌人,只需在Cube上添加上一个行为树,然后添加Action下的Patrol,然后设置一下Patrol的参数,即可实现。
ps:行为树插件依赖Unity的Nav导航系统,因此需要将Plane设置为Nav静态,然后给Cube添加上NavMeshAgent组件即可。
我们可以看到Patrol为绿色,代表它一直在执行
案例2:怪物在巡逻,玩家向目标点走去,如果路上看见了怪物,那么则改为追怪物。
要完成上述这个要求,我们需要用到一个Selector(判断玩家是去往目标点走去,还是追寻怪物,二选一),一个Sequence(用来判断是否发现了怪物,发现了则追击),两个Action下的Seek(用处是去往目标点,一个是去往最初设定的目标点,一个是去往怪物的目标点),一个Conditionals下的Can See Object,判断是否发现了目标。
详细步骤如下图所示:
如图所示:最初Can See Object一直是叉,但是有一个红圈,代表它一直在检测,此处就是因为我们将Sequence中的Type改为Lower Priotity,左边权重比右边高,则会打断右边。
Abort模式还有Self和 Both。
Self代表可以打断自身。例如最初检测到了怪物,玩家追寻怪物,但是玩家速度比怪物慢,因此没追上,脱离了视野,那么此时不满足Can See的条件,就会打断,而向最初设定的目标点走去。
Both代表同时执行Self和Lower Priotity。
此处不在演示,感兴趣的小伙伴可以自己试一下。
Demo案例
不同行为树之间的切换
实际项目中,一个物体可能有多种不同的状态,不同的状态下行为有些许差异,我们虽然可以写成一个行为树(想想就好复杂),因此,行为树插件支持一个物体拥有多个行为树,但是同时只能运行一个。
下面拿一个简单的Demo举例。
一个员工,需要从A点到达B点工作,A点是家,B点是工厂。
逻辑为 员工从A点出发,跑向工厂,到达工厂工作一段时间后跑回家。
员工可能今天干的活是修瓷砖,明天是砌墙,俩者之间仅仅有此不同,因此我们就可以创造两个行为树,只对这个动画做区分。
public class AniTest : MonoBehaviour
{
Animator animator;
public Transform objPosition;
public List<BehaviorTree> FirestS = new List<BehaviorTree>();
public List<BehaviorTree> Seconds = new List<BehaviorTree>();
private void Awake()
{
animator = GetComponent<Animator>();
BehaviorTree[] trees= FindObjectsOfType<BehaviorTree>();
foreach (var item in trees)
{
if(item.Group == 1)
{
FirestS.Add(item);
}
else
{
Seconds.Add(item);
}
}
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.H))
{
foreach (var item in Seconds)
{
item.DisableBehavior();
}
foreach (var item in FirestS)
{
item.EnableBehavior();
}
}
if (Input.GetKeyDown(KeyCode.J))
{
foreach (var item in FirestS)
{
item.DisableBehavior();
}
foreach (var item in Seconds)
{
item.EnableBehavior();
}
}
}
void EventTest()
{
Debug.Log("执行了动画事件");
}
}
可以看到,First行为树最初是绿色,然后变成灰色,此时是因为我通过代码控制,切换到了另一个行为树Second,切换到那个行为树发现是绿色的。
并且做得也是Second的动作(挥手,First是散步)。
行为树插件的功能很强大,我目前了解到的只是冰山一角,还可以和动画系统中自带的事件系统起到意想不到的效果。控制行为树插件播放动画,动画播完之后可以执行一些事件。
此处其实是根据我实际工作项目经验改了一下,之前工作要控制工人运动,路程大差不差,缺的就是最后运行的动画。