手把手教你使用Unity的Behavior Designer

一、什么是行为树

如果了解过状态机,会知道在行为树之前,在实现AI用得比较多的技术是状态机,状态机理解起来是比较简单的,即一个状态过渡到另一个状态,通过判断将角色的状态改变即可,如果学习过Unity的Mecanim动画系统,会更加直观的理解。
但是状态机在状态较多的情况下会使状态之间的切换变得异常繁琐,同时状态之间很难复用。
在这种情况下,行为树被发明出来。

 

二、行为树的优点

1.行为树提供大量的流程控制方法,使得状态之间的改变更加直观;
2.整个游戏AI使用树型结构,方便查看与编辑;
3.方便调试和代码编写;
4.最重要的:行为树方便制作编辑器,可以交由策划人员使用。

 

三、用行为树就可以不写代码了吗

Behavior Designer是Unity制作行为树的一个插件,它的主要作用并非可以不写代码(还是要写不少代码的),而是能让游戏中逻辑最混乱的模块——AI模块能更有序的组织,方便查看、调试和修改。

 

接下来跟着我一起学Behavior Designer吧!

 

四、Behavior Designer下载

注:本文的Demo工程我已上传到CSDN和GitHub,使用的Unity版本是2020.1.2f1c1,各位同学可以下载下来学习。

AssetStore下载 (付费版): https://assetstore.unity.com/packages/tools/visual-scripting/behavior-designer-behavior-trees-for-everyone-15277

CSDN Demo工程(附带Behavior Designer插件):https://download.csdn.net/download/linxinfa/12821588

GitHub Demo工程(附带Behavior Designer插件):https://github.com/linxinfa/Unity-BehaviorDesigner-Demo

Behavior Designer是Unity的一个制作行为树的插件,网上使用教程比较少,今天就来写下教程好了,毕竟是个好东西,跟大家分享下。

五、界面介绍

导入Behavior Designer后,可以在 Tools - Behavior Designer - Editor 打开编辑器,如下图所示

编辑器视图如下

1 部分

行为树的组织区域

2部分

一堆Task,主要先掌握Composities(符合节点)、Decorators(装饰节点)和Actions(行为节点),如何组织是难点

3部分

Behavior标签,可以设置行为树的一些属性

注意:这个Restart When Complete,如果勾选了,则行为树遍历完后会再重新启动一次,不断循环,为了测试方便,我勾选上,一般情况下,也需要让行为树一直运行。

这些属性也可以通过代码进行设置,一般我们编辑好行为树后会导成asset文件作为ExternalBehavior,这样可以复用这个行为树。

假设我们已经导出在Resources目录下,叫Behavio.asset,然后现在有一个cube,我们想给这个cube绑上行为树,我们可以这样做

// 使用代码动态添加行为树
var bt = cube.AddComponent<BehaviorTree> ();
// 注意,这里使用Resources.Load方法,所以“Behavior”这个资源必须放在Assets/Resources这个目录中
var extBt = Resources.Load<ExternalBehaviorTree> ("Behavior");
bt.StartWhenEnabled = false;
// 给行为树设置我们事先编辑好的行为树文件 (以ExternalBehavior的形式)
bt.ExternalBehavior = extBt;

注意:以上用到了Resources.Load方法,所以行为树资源务必放在工程目录Assets/Resources中,如果没有Resources目录,则在Assets目录中新建一个Resources目录。如果对Unity的路径操作不清楚的,可以参见我的这篇文章:https://blog.csdn.net/linxinfa/article/details/51679528

关于StartWhenEnabled的值:
其中之所以把StartWhenEnabled置为false,是因为我们不想让行为树立刻启动,我们可能有一些初始化操作需要进行
如果无需做额外的初始化,也可直接设置bt.StartWhenEnabled=true,否则行为树启动后运行一次就不在循环运行了。
我们做完初始化操作后,可以通过bt.EnableBehavior ();来启动行为树。

Variables标签,可以给行为树添加变量,这些变量可以通过GetVariableSetVariableValue来读取和赋值,并可以在各种Task节点里进行传递和赋值,这个后面讲下方法。

Inspector标签就是Task的检视窗口啦,我们可以在这个标签里设置Task的属性什么的

4部分

这里是当前的行为树物体和对象,可以点击进行切换不同的行为树物体和行为树对象,一个物体可以绑定多个行为树,不过一般只绑一个就可以了,左边的左右箭头可以切换行为树物体,右边的+-号可以增加和删除行为树。

5部分

Export,就是把行为树导出成外部文件,可以选择二进制文件或者Json文件,导出的格式可以在6部分那里进行设置。导出后是一个xxxx.asset文件,它是作为ExternalBehaviorTree存储的,使用的时候要注意下。

二进制格式

json格式

6部分

主要就是那个导出文件的格式设置啦,如下图:

六、具体使用

简单说完了界面,我们现在开始讲下具体使用吧。

第一步,给一个物体添加行为树

方法1:打开行为树编辑器,然后选中要添加行为树的物体,然后在编辑器编辑区右键Add Behavior Tree

方法2:直接给物体AddComponent一个BehaviorTree组件,然后点击Open打开即可进行编辑

添加了行为树,现在以一个简单的例子讲下树的制作。

假设我们要做一个吃饭、睡觉、地震啦逃跑的行为树,可以想象一下,我们需要有事件发送和接受,比如我们告诉小a说你去吃饭吧,那么他收到事件后就去执行吃饭,我们发送时间告诉小a说去睡觉吧,他就去睡觉了,我们要需要中断,比如正在睡觉,突然地震啦,赶紧逃命。

行为树的组织图如下

具体步骤:

1 首先点击Unity顶部菜单栏Tools - Behavior Designer - Editor,打开行为树编辑器

2 在Hierarchy窗口中鼠标右键 - Create Empty,创建一个GameObject

3 如果没有选中任何物体,行为树编辑器(上面说到的1区域)中会显示 “Select a GameObject”,我们要给上面的GameObject添加行为树,所以选中GameObject,此时会显示 "Right Click,Add a Behavior Tree Component",我们在行为树窗口中右键->Add Behavior Tree,此时会看到GameObject被添加了BehaviorTree组件,编辑器中显示“Add a Task”,接下来我们就可以开始添加节点了

4 我们先添加一个Selector节点,找到上文说的2区域,在Composites标签列表中找到Selector,点击,即可看到在组织区域中多了一个Entry节点和一个Selector节点,并且它们之间用连线连了起来。Selector节点就像or逻辑一样,它从左到右执行,遇到success则立刻返回success,如果遇到failure,则继续执行右边的节点。

5 接下来添加Sequence节点,同理,它在Composites标签中,点击Sequence,会在组织区域中创建一个Sequence节点,注意它可能被之前的节点挡住,可以稍微一动一下节点的位置。Sequence节点就像and逻辑,从左到右执行,遇到failure则立刻返回failure,如果遇到success则继续执行下一个节点,直到从左到右所有子节点都返回success,它才返回success。

6 创建事件接收节点:Has Received Event,它在Conditionals标签中,如果很难找,可以在2区域中上面的搜索框里直接输入名字查找,有个小小的放大镜图标那里。Has Received Event用来做事件接收检测,事件已字符串作为标识,比如事件"Eat"这样子,事件自己来定义。点中刚刚创建的Has Received Event节点,然后在3区域中点Inspector标签,可以看到节点的属性,我们会看到Event Name这个属性,这就是事件标识,在这里设置好Event Name,我这里做了三个事件:"Eat","Sleep","EarthQuake"

7 添加Log节点,它被执行的时候会输出log,打log的内容可以在它的节点属性Text设置

8 新创建两个自定义的节点Eat和Sleep,一般我们在做特殊行为的时候,都需要自己拓展出一些新自定义的节点,来实现对应的需求。Eat和Sleep的代码看下文,它要继承Action,具体代码见下文。

9 新建行为树变量,在3区域中点击Variables标签,会看到Name,Type和一个Add按钮,在Name中输入变量的名字,在Type中选择变量的类型,然后点击Add按钮即可添加变量。我们添加一个String类型的food变量,和一个Int类型的sleepTime变量。

行为树的变量如下:

行为Eat和Sleep是另外写的Action,代码如下:

// Eat.cs,自定义节点:吃饭。注意要继承Action

using UnityEngine;
using System.Collections;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

public class Eat : Action
{
    // 变量:食物。会在行为树编辑器中赋值
    public SharedString food;

    public override TaskStatus OnUpdate()
    {
        Debug.Log("eat: " + food.GetValue());
        return TaskStatus.Success;
    }
}

 

// Sleep.cs,自定义节点:睡眠
 
using UnityEngine;
using System.Collections;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;

public class Sleep : Action
{
    // 睡眠时间。会在行为树编辑器中赋值
    public SharedFloat sleepTime;

    private float m_sleepTime;
    private float m_startTime;

    public override void OnStart()
    {
        m_startTime = Time.time;
        m_sleepTime =(float)sleepTime.GetValue();
    }


    public override TaskStatus OnUpdate()
    {
        if(m_startTime + m_sleepTime < Time.time) 
        {
            return TaskStatus.Success;
        }
        Debug.Log("I am Sleeping");
        return TaskStatus.Running;
    }
}


其中Eat的属性如下

同理Sleep的属性如下:

关于中断:
需要注意一个点,就是Task中断,中断有3种类型:Self,Lower Priority,Both
怎么理解中断呢,行为树是从上到下从左到右执行的,假设执行到右边第二个节点,这个节点假设是一个持续运行的节点,比如“睡觉”,而此时突然收到一个“”地震”的消息,就必须立刻中断“睡觉”。
Self就是中断自己的意思;
Lower Priority是中断比自己低权限的节点,在行为树种,右边的节点比左边的节点权限低;
Both就是中断自己和比自己低权限的节点。
只有复合节点(Composites标签中的那些节点,比如上面的Selector和Sequence)有中断属性。我们选中Sequence,在它的Inspector中可以看到Abort Type属性,我们设置成Loawr Priority。

行为节点的摆放位置:
我们地震的优先级最高,所以放在最左边,优先级最低的是睡觉,地震要中断吃饭跟睡觉,吃饭要中断睡觉

我们把上面的行为树导成二进制文件,放到Resources目录下。(注:如何把行为树导成二进制文件?上文的5区域有个Export按钮,点它就可以导出了,这个二进制文件怎么用起来呢?可以通过代码动态绑定到物体上,也可以直接在物体上添加Behavior Tree组件然后把二进制文件拖到External Behavior属性中)

接下来,现在我们开始写控制代码:

我们创建一个空物体(Create Empty),把它命名为rannerObj,然后新建一个Runner.cs脚步挂在rannerObj上,代码如下:

// Runner.cs

using UnityEngine;
using System.Collections;
using BehaviorDesigner.Runtime;


public class Runner : MonoBehaviour {

    private BehaviorTree m_bt;

    void Start() 
    {
        // 动态添加行为树
        var bt = gameObject.AddComponent<BehaviorTree>();
        // 加载行为树资源
        var extBt = Resources.Load<ExternalBehaviorTree>("Behavior");
        bt.StartWhenEnabled = false;
        bt.RestartWhenComplete = true;
        // 设置行为树
        bt.ExternalBehavior = extBt;
        bt.EnableBehavior();
        // 把行为树对象缓存起来,后面需要通过它来设置变量和发送时间
        m_bt = bt;
    }
    

    void Update() 
    {
        if(Input.GetKeyDown(KeyCode.A)) 
        {
            // 吃红烧牛肉面
            m_bt.SetVariableValue("food","红烧牛肉面");
            m_bt.SendEvent("Eat");
        }
        if(Input.GetKeyDown(KeyCode.B)) 
        {
            // 睡10000秒
            m_bt.SetVariableValue("sleepTime",10000f);
            m_bt.SendEvent("Sleep");
        }
        if(Input.GetKeyDown(KeyCode.H))
        {
            // 地震啦
            m_bt.SendEvent("EarthQuake");
        }
    }
}

测试效果如下:

这只是一个简单的例子,还有很多复杂的行为树,等后面有事件研究了再进行补充。

好啦,今天就先写到这里吧。

相关文章推荐:

Behavior Designer 中文版教程:https://www.jianshu.com/p/64b5fe01fb1c

 

 

 

原文地址:https://linxinfa.blog.csdn.net

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


这篇文章将为大家详细讲解有关Unity3D中如何通过Animator动画状态机获取任意animation clip的准确播放持续时长,小编觉得挺实用的,因此分享给大家做个参考,
这篇文章主要介绍了Unity3D如何播放游戏视频,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解
这篇文章给大家分享的是有关Unity3D各平台路径是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1、Resources路径 Reso...
小编给大家分享一下Unity3D如何实现移动平台上的角色阴影,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!由于目前主流使用Unity3.x在移动平...
如何解析基于Unity3D的平坦四叉树地形与Virtual Texture的分析,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希
这篇文章主要介绍Unity3D如何实现动态分辨率降低渲染开销,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!之前项目降低分辨率我们都普...
这篇文章主要介绍了unity3d中如何使用屏幕空间改善shadowmap漏光,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编...
这篇文章主要介绍unity3d如何实现基于屏幕空间的描边,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!Outline(Based on Image Space)由...
这篇文章给大家分享的是有关unity3d中导入fbx时的Scale是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。在Unity中点击GameOb...
这篇文章主要为大家展示了“unity3d中如何实现ttc转ttf及制作字体”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习
这篇文章主要介绍了unity3d中水彩风渲染有什么用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了...
这篇文章将为大家详细讲解有关unity3d中图像压缩原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1 图像可压缩...
这篇文章给大家分享的是有关unity3d中光照公式有哪些的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。漫反射、高光、物理渲染(PBR...
小编给大家分享一下unity3d中光照探针的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我...
这篇文章将为大家详细讲解有关Unity3D中Rendering Paths及LightMode的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有
这篇文章将为大家详细讲解有关unity3d中图形学的光照原理是什么,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。首先,在...
这篇文章给大家分享的是有关unity3d中图片渲染流程是什么的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。相关名词GPU(Graphic Pr...
本篇我们来介绍一下左侧工具栏中基本绘制的应用。 一、墙体绘制直墙 & 矩形墙绘制墙体时,可以看到上方的工具栏中对墙体进行参数的设定。 弧形墙在建筑版的户...
xlua是由腾讯维护的一个开源项目,我们可以在github上下载这个开源项目并查看一些相关文档官网:https://github.com/Tencent/xLua配置文档:https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/hotfix.md常见问题解答:https://github.com/Tencent/xLua/blob/master/Assets/
我们都知道,一个三维场景的画面的好坏,百分之四十取决于模型,百分之六十取决于贴图,可见贴图在画面中所占的重要性。在这里我将列举一些贴图,并且初步阐述其概念,理解原理的基础上制作贴图,也就顺手多了。我在这里主要列举几种UNITY3D中常用的贴图,与大家分享,希望对大家有帮助。01 首先