BehaviorDesigner——行为树,用于控制和实现AI逻辑
一 Behavior:这个行为树的设置
二 Tasks:行为树的所有节点Tasks
Behavior Designer插件里,主要有四种概念节点,都称之为Task。包括:
(1) Composites 组合节点
包括经典的:Sequence,Selector,Parallel
Abort Type 中止类型
复合类Task的优先级和打断:
这一点非常重要,是复合类Task的唯一特殊属性:分为四种——不打断,可打断自身,可打断低于该Task优先级的其他Task,既可以打断自身也可以打断低于其优先级的。
需要注意的是,该复合节点的打断条件是其下子节点必须有条件节点,此时该条件节点的判断一直处于运行状态,一旦该条件节点在某一刻发生改变,此时行为树将重新跳转到该复合节点位置继续运行,从而打断其他正在运行的低优先级节点。
(2) Decorator 装饰节点
,顾名思义,就是为仅有的一个子节点额外添加一些功能,
比如让子task一直运行直到其返回某个运行状态值,或者将task的返回值取反等等
(3) Actions 行为节点
,行为节点是真正做事的节点,其为叶节点。Behavior Designer插件中自带了不少 Action节点,如果不够用,也可以编写自己的Action。一般来说都要编写自己的Action,
除非用户是一个不懂脚本的美术或者策划,只想简单地控制一些物件的属性。
(4) Conditinals 条件节点
,用于判断某条件是否成立。目前看来,是Behavior Designer为了贯彻职责单一的原则,
将判断专门作为一个节点独立处理,比如判断某目标是否在视野内,其实在攻击的Action里面也可以写,但是这样
Action就不单一了,不利于视野判断处理的复用。一般条件节点出现在Sequence控制节点中,其后紧跟条件成立后的Action节点。
自定义Task任务:
一般复合类和装饰类的Task是够用的,甚至有些根本用不到,而具体的行为类Task和条件类Task从来都不能满足我们的需求,而且自己写这类Task可以很大程度的简化整个行为树结构。
自己写Task的步骤如下:
1.引入命名空间:
1 using BehaviorDesigner.Runtime;
2 using BehaviorDesigner.Runtime.Tasks;
2.明确继承的Task类型:
1 public class MyInputMove : Action
2 public class MyIsInput : Conditional
观察上图就会发现和Unity中编写脚本大同小异,不一样的地方就是这里的Update有返回值,要返回该任务的执行状态,只有在Running状态时才每帧调用。
using UnityEngine;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
public class MyInputMove : Action
{
public SharedFloat speed = 5f;
public override TaskStatus OnUpdate()
{
float inputX = Input.GetAxis("Horizontal");
float inputZ = Input.GetAxis("Vertical");
if (inputX != 0 || inputZ != 0)
{
Vector3 movement = new Vector3(inputX, 0, inputZ);
transform.Translate(movement*Time.deltaTime*speed.Value);
return TaskStatus.Running;
}
return TaskStatus.Success;
}
}
还有一点不同,就是Task中封装了一层Share的类型的属性,它和非Share类型有何区别呢?
下面是对比:
using UnityEngine;
using BehaviorDesigner.Runtime.Tasks;
using BehaviorDesigner.Runtime;
public class MyLog : Action
{
public string staticLog;
public SharedString shareLog;
public override TaskStatus OnUpdate()
{
Debug.Log(staticLog + shareLog.Value);
return TaskStatus.Success;
}
}
还有一点不同,就是Task中封装了一层Share的类型的属性,它和非Share类型有何区别呢?
下面是对比:
using UnityEngine;
using BehaviorDesigner.Runtime.Tasks;
using BehaviorDesigner.Runtime;
public class MyLog : Action
{
public string staticLog;
public SharedString shareLog;
public override TaskStatus OnUpdate()
{
Debug.Log(staticLog + shareLog.Value);
return TaskStatus.Success;
}
}
可以看到,这里的Share的类型就是一个方便在行为树中传递和修改的变量,因为Task之间是不方便直接修改其他Task变量的,那怎么办呢,于是就增加一种Share的类型变量在行为树的各个Task之间进行交流。
比如这里,每次找到的最近的敌人是不一样的,要根据上一个Task返回的值去执行下一个Task的攻击或打印结果,这时固定的属性就无法满足要求,但直接调用别的Task又增加了耦合性,于是就单独用Share变量来传递值。这样也方便在面板中统一查看管理。
另外Share变量也可以增加自定义类型,全局的和本地变量的区别就是一个在所有的行为树中有,一个只有这棵树中有。
在取Share变量值时需要.Value。
三 Variables:行为树的 通用变量
用于这个行为树所有节点间变量的 通信(比如A节点的是寻敌任务,寻找到敌人Target后把这
个Target设置到通用变量中,B节点的任务从这个通用变量中拿到Target执行攻击任务
四 Inspector:
点击某个节点后,显示节点的详细信息,可以设置 public 变量