BehaviorDesigner——行为树,用于控制和实现AI逻辑

unity lua 行为树 unity行为树插件_unity lua 行为树

一 Behavior:这个行为树的设置

二 Tasks:行为树的所有节点Tasks

Behavior Designer插件里,主要有四种概念节点,都称之为Task。包括:

(1) Composites 组合节点

包括经典的:Sequence,Selector,Parallel

unity lua 行为树 unity行为树插件_unity lua 行为树_02

Abort Type 中止类型

复合类Task的优先级和打断:

这一点非常重要,是复合类Task的唯一特殊属性:分为四种——不打断,可打断自身,可打断低于该Task优先级的其他Task,既可以打断自身也可以打断低于其优先级的。

unity lua 行为树 unity行为树插件_unity lua 行为树_03


需要注意的是,该复合节点的打断条件是其下子节点必须有条件节点,此时该条件节点的判断一直处于运行状态,一旦该条件节点在某一刻发生改变,此时行为树将重新跳转到该复合节点位置继续运行,从而打断其他正在运行的低优先级节点。

(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 lua 行为树 unity行为树插件_行为树_04


观察上图就会发现和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;
    }
}

unity lua 行为树 unity行为树插件_行为树_05


unity lua 行为树 unity行为树插件_unity_06


可以看到,这里的Share的类型就是一个方便在行为树中传递和修改的变量,因为Task之间是不方便直接修改其他Task变量的,那怎么办呢,于是就增加一种Share的类型变量在行为树的各个Task之间进行交流。

比如这里,每次找到的最近的敌人是不一样的,要根据上一个Task返回的值去执行下一个Task的攻击或打印结果,这时固定的属性就无法满足要求,但直接调用别的Task又增加了耦合性,于是就单独用Share变量来传递值。这样也方便在面板中统一查看管理。

另外Share变量也可以增加自定义类型,全局的和本地变量的区别就是一个在所有的行为树中有,一个只有这棵树中有。

在取Share变量值时需要.Value。

三 Variables:行为树的 通用变量

用于这个行为树所有节点间变量的 通信(比如A节点的是寻敌任务,寻找到敌人Target后把这
	个Target设置到通用变量中,B节点的任务从这个通用变量中拿到Target执行攻击任务

四 Inspector:

点击某个节点后,显示节点的详细信息,可以设置 public 变量