unity-Behavior Designer行为树插件


效果图
这里写图片描述


  • 导入插件 Behavior Designer 1.5.7,打开编辑面板

Tools -> Behavior -> Designer -> Editor

![这里写图片描述](https://pic01.wilker.cn/20180421_150404_224.png)

-  Behavior:这个行为树的设置
- Tasks:行为树的所有节点Tasks(少用,一般直接在编辑器右键即可)
- Variables:行为树的 **通用变量**,用过这个行为树所有节点间变量的 **通信**(比如A节点的是寻敌任务,寻找到敌人Target后把这个Target设置到通用变量中,B节点的任务从这个通用变量中拿到Target执行攻击任务,在ue4中就是 **BlackBoard**)
- Inspector:点击某个节点后,显示节点的详细信息,可以设置 **public** 变量

  • 效果图的设计是:(markdown的流程图语法有点不熟就不用了)
    没有敌人 -> 半径范围内寻找敌人 -> 没找到就发呆几秒 -> 范围内随机移动

1. 给 gameobject 穿件 behavior tree

  1. 选中场景中的一个对象 Capsule,然后在行为树编辑器右键 Add Behavior Tree

    这里写图片描述

    • Instant;勾选这个选项意思同一帧执行这个节点,不勾选则下一帧执行

2. 创建一个 SharedTransform类型 的通用变量 target

这里写图片描述


3. 创建各个节点如下

这里写图片描述

  1. Conditional Evaluator 评估节点:判定是否有 Target,里面的 Condictional 脚本是

    HasTarget.cs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    using UnityEngine;
    namespace BehaviorDesigner.Runtime.Tasks
    {
    [TaskDescription("Returns success as soon as the event specified by eventName has been received.")]
    [HelpURL("http://www.opsive.com/assets/BehaviorDesigner/documentation.php?id=123")]
    [TaskIcon("{SkinColor}HasReceivedEventIcon.png")]
    [TaskCategory("My")]
    public class HasTarget : Conditional
    {
    public override void OnStart()
    {

    }

    public override TaskStatus OnUpdate()
    {
    SharedTransform target = (SharedTransform)Owner.GetVariable("target");
    if (target.Value == null)
    {
    Debug.Log("--- HasTarget, false");
    return TaskStatus.Failure;
    }
    else{
    Debug.Log("--- HasTarget, true");
    return TaskStatus.Success;
    }
    }
    }
  2. Search Target 条件节点:用来寻找敌人,脚本是
    SearchTarget.cs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    using System.Collections;
    using UnityEngine;
    namespace BehaviorDesigner.Runtime.Tasks.Basic.UnityTransform
    {
    [TaskCategory("My")]
    public class SearchTarget : Conditional
    {
    public Transform mTarget = null;
    public float mRadius = 2f;

    public override void OnStart()
    {
    Collider[] hits = Physics.OverlapSphere(transform.position, mRadius);
    for(int i = 0; i < hits.Length; ++i)
    {
    if (hits[i].transform.CompareTag("Player"))
    {
    mTarget = hits[i].transform;
    Owner.SetVariableValue("target", mTarget);
    break;
    }
    }
    }

    public override TaskStatus OnUpdate()
    {
    return mTarget ? TaskStatus.Success : TaskStatus.Failure;
    }
    }
    }
  3. Wait 动作节点:默认自带的 Action 节点
    右键 Add Task -> Actions -> Wait

  4. Range Move 动作节点:范围内随机移动,脚本是
    RangeMove.cs

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    using UnityEngine;
    using System.Collections;
    using DG.Tweening;
    namespace BehaviorDesigner.Runtime.Tasks
    {
    [TaskDescription("Returns success as soon as the event specified by eventName has been received.")]

    [TaskCategory("My")]
    [TaskIcon("Assets/bdIcon/aaa.jpg")]
    public class RangeMove : Action
    {
    public bool mIsOver = false;
    public float mRange = 2f;
    public float mTotalTime = 2f;

    public override void OnStart()
    {
    float x = Random.Range(-mRange, mRange);
    float z = Random.Range(-mRange, mRange);
    Vector3 dir = transform.position + new Vector3(x, 0, z); //dest world position
    transform.LookAt(dir);
    transform.DOLocalMoveX(x, mTotalTime).OnComplete(()=> { mIsOver = true; });
    transform.DOLocalMoveZ(z, mTotalTime);
    }

    public override TaskStatus OnUpdate()
    {
    return mIsOver ? TaskStatus.Success : TaskStatus.Running;
    }
    }
    }

备注

  • 运行时各个节点只有public变量才会被 reset 成默认的值
    OnReset方法只是在编辑器中的inspector中,点击reset时调用,并不是运行时被调用