游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)
Posted 林新发
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)相关的知识,希望对你有一定的参考价值。
文章目录
- 一、前言
- 二、插件下载
- 三、官方教程
- 四、插件界面
- 五、快速制作一棵行为树
- 六、复合节点:Composities
- 七、中断
- 八、Variables:行为树成员变量
- 九、行为树事件
- 十、拓展行为树节点
- 十一、完毕
一、前言
嗨,大家好,我是新发。之前用过Behavior Designer
行为树插件,最近又需要用到,我发现网上的教程很多写得不够好,今天我就来写写Behavior Designer
行为树的教程吧,希望可以帮到大家。
二、插件下载
1、AssetStore下载
插件可以在AssetStore
下载到,
地址:https://assetstore.unity.com/packages/tools/visual-scripting/behavior-designer-behavior-trees-for-everyone-15277
目前最新的版本是1.7.2
,
可以在官网查看每个版本的迭代内容:
https://opsive.com/news/category/release-notes/behavior-designer-release-notes/
2、GitCode下载
我放了1.7.1
版本的到GitCode
上,地址:https://gitcode.net/linxinfa/unitybehaviordesigner
下载下来后,放入你的工程即可,
其中我加了一个FixGUIStyle
脚本,
因为在Unity
专业版上,打开Behavior Designer
编辑器是这个鬼样,
注:如果你使用的是个人版,则不会有这个问题
它的GUIStyle
在Unity
专业版本有问题,插件编辑器是个dll
文件,我们无法修改编辑器源码,
然后我进行了反编译,找到了它的封装各种GUIStyle
的类:BehaviorDesignerUtility
,
通过反射,修改这个类的私有静态成员对象,代码如下:
using UnityEngine;
using UnityEditor;
using System.Reflection;
using BehaviorDesigner.Editor;
/// <summary>
/// 修复BehaviorDesigner编辑器的GUIStyle问题
/// </summary>
public class FixGUIStyle
[MenuItem("Tools/Behavior Designer/修复界面GUIStyle #b", false, 0)]
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void ShowWindow()
var guiStyle = new GUIStyle();
guiStyle.alignment = TextAnchor.UpperCenter;
guiStyle.fontSize = 12;
guiStyle.wordWrap = true;
guiStyle.normal.textColor = Color.white;
guiStyle.active.textColor = Color.white;
guiStyle.hover.textColor = Color.white;
guiStyle.focused.textColor = Color.white;
Texture2D tex2D = new Texture2D(1, 1, TextureFormat.RGB24, false);
tex2D.SetPixel(1, 1, new Color(0.1f, 0.1f, 0.1f, 1f));
tex2D.hideFlags = HideFlags.HideAndDontSave;
tex2D.Apply();
guiStyle.normal.background = tex2D;
// 反射动态修改私有静态成员,修改GUIStyle
var t = typeof(BehaviorDesignerUtility);
var graphBackgroundGUIStyle = t.GetField("graphBackgroundGUIStyle", BindingFlags.Static | BindingFlags.NonPublic);
graphBackgroundGUIStyle.SetValue(null, guiStyle);
var taskCommentGUIStyle = t.GetField("taskCommentGUIStyle", BindingFlags.Static | BindingFlags.NonPublic);
taskCommentGUIStyle.SetValue(null, guiStyle);
var selectedBackgroundGUIStyle = t.GetField("selectedBackgroundGUIStyle", BindingFlags.Static | BindingFlags.NonPublic);
selectedBackgroundGUIStyle.SetValue(null, guiStyle);
var preferencesPaneGUIStyle = t.GetField("preferencesPaneGUIStyle", BindingFlags.Static | BindingFlags.NonPublic);
preferencesPaneGUIStyle.SetValue(null, guiStyle);
点击菜单Tools/Behavior Designer/修复界面GUIStyle
,或者按一下Shift+B
快捷键,即可修复GUIStyle
问题,
效果如下
三、官方教程
学习插件的时候,建议先看下官方文档。
1、在线文档
官方在线文档: https://opsive.com/support/documentation/behavior-designer/overview/
2、离线文档
插件的Behavior Designer
目录中有个Documentation.pdf
文档,不过可能很多同学都没去看这份文档,建议还是看一看,
四、插件界面
1、打开编辑器
点击菜单Tools/Behavior Designer/Editor
即可打开编辑器窗口,
如下
2、界面介绍
界面可以划分为3
个区域,如下
第1
个区域是行为树的组织区域,我们的行为树节点就是在这个区域上进行连线的;
第2
个区域顶部是四个标签页:Behavior
、Tasks
、Variables
、Inspector
,默认是Tasks
标签页,显示的是节点列表;
标签页 | 说明 |
---|---|
Behavior | 对整个行为树的设置,比如行为树名称、是否激活时立即执行、执行完毕后是否重新执行等设置 |
Tasks | 任务节点列表,比如Composities(符合节点)、Decorators(装饰节点)、Actions(行为节点)等等 |
Variables | 行为树变量,可以设置全局变量,也可以设置行为树自身的变量 |
Inspector | 查看节点详细信息,设置节点参数 |
第3
个区域按钮功能如下
五、快速制作一棵行为树
在讲解节点之前,我先演示一遍如何快速制作一棵行为树,让大家对整个流程有个概念。
1、创建物体
首先在场景中创建一个物体,可以是任何物体,比如你想控制角色,那么这个物体就可以是你的角色模型,这里我创建一个空物体,
重命名为BehaviorTreeObj
,
2、挂BehaviorTree脚本
打开Behavior Designer
编辑器,先选中刚刚的BehaviorTreeObj
物体,然后在编辑器网格空白处右键鼠标,点击菜单Add Behavior Tree
,
这一步其实就是给物体挂上BehaviorTree
组件,我们也可以通过Add Component
手动添加BehaviorTree
组件,
3、添加Task节点
我以最简单的Log
节点为例,它的功能就是输出日志,在空白处点击鼠标左键,按键盘的空格键
即可弹出节点搜索框,搜索log
,可以看到Log
节点,点击即可创建出Log
任务节点,它会自动帮我添加一个Entry
入口节点,Entry
连向Log
,如下
我们给Log
节点设置Text
字段的值为Hello World
,并添加注释,如下
4、运行测试
运行Unity
,可以看到Log
节点被执行了,显示了一个绿色的勾,日志窗口也输出了HelloWorld
,
5、导出BehaviorTree
点击Export
按钮,将行为树导出保存为资源文件,
如下,这样子就可以复用行为树资源了,
6、手动引用BehaviorTree树资源
6.1、设置External Behavior
我们把上面导出的行为树资源赋值给BehaviorTree
组件的External Behavior
成员,如下
运行时,就会根据这个External Behavior
的行为树来执行了。
6.2、使用Behavior Tree Reference
另外,我们还可以通过Behavior Tree Reference
节点来引用行为树资源,
先创建一个Behavior Tree Reference
节点,点击Inspector
,设置它引用的外部行为树资源即可,
一般我们会做一个通用的行为树保存为资源文件,然后对特殊的怪物另外做行为树,并让其引用通用行为树资源,像这样子
7、动态加载行为树资源并设置External Behavior
我们也可以通过代码动态设置External Behavior
,我这里使用Addressables
插件来做资源加载,
注:关于
Addressables
的使用,可以参见我之前写的这篇文章:【游戏开发探究】Unity Addressables资源管理方式用起来太爽了,资源打包、加载、热更变得如此轻松(Addressable Asset System | 简称AA)
首先给行为树资源文件添加Addressable
管理,分配到一个Addressable
索引名,如下
注:勾上Addressable就会自动分配一个索引名了
接着我们可以使用代码加载行为树资源,动态赋值给BehaviorTree
组件的ExternalBehavior
对象,
创建一个AddBehaviorTree.cs
脚本,
代码如下
using System.Collections;
using UnityEngine;
using BehaviorDesigner.Runtime;
using UnityEngine.AddressableAssets;
public class AddBehaviorTree : MonoBehaviour
IEnumerator Start()
// 初始化Addressables
yield return Addressables.InitializeAsync();
// 添加BehaviorTree
var bt = gameObject.AddComponent<BehaviorTree>();
// 设置不立即执行
bt.StartWhenEnabled = false;
// 加载行为树资源
var loader = Addressables.LoadAssetAsync<ExternalBehaviorTree>("Assets/BtRes/Behavior.asset");
yield return loader;
// 设置ExternalBehavior
bt.ExternalBehavior = loader.Result;
// 执行行为树
bt.EnableBehavior();
我们创建一个空物体,重命名BtObj
,并给它挂上AddBehaviorTree
脚本,如下
我们运行Unity
,可以看到BtObj
物体动态挂了BehaviorTree
组件,并且动态设置了ExternalBehavior
对象,行为树也被执行了,输出了Hello World
,如下
我们可以点击BehaviorTree
组件上的Open
按钮,
可以看到行为树已经执行完毕,
如果我们想让行为树执行完毕后重复执行,可以设置RestartWhenComplete
为true
,
我们串一个Wait
节点,方便观察,
执行效果如下,可以看到行为树可以重复执行了,
上面我们用到了Squence顺序节点,它是一个复合节点,下文我会进行节点的详细介绍。
六、复合节点:Composities
行为树任务节点中最重要的节点就是复合节点了,它决定了行为树的执行流,也就是Tasks
列表中Composities
分组的这些节点,
我先简单做个日常生活的行为树给大家看看,如下
上面的行为树的执行过程是怎么样的呢?想要搞清楚就得先知道行为树的执行顺序:从左到右,并且深度优先。
另外,复合节点又控制着子节点的执行规则,是顺序执行还是并行执行,亦或者随机执行,是遇到成功就立即返回成功还是遇到失败就立即返回失败,亦或者遇到成功继续执行下一个,或者遇到失败继续执行下一个呢?
这些都是复合节点来控制的,这里头的逻辑需要大家好好琢磨,下面我带大家挨个过一遍复合节点,讲解过程中我会穿插顺带讲解一下用到的其他任务节点,并举一些实际的应用场景,方便大家学以致用,也希望大家能够触类旁通。
1、Sequence:顺序节点
1.1、节点介绍
顾名思义,顺序节点就是从左到右挨个顺序执行,当所有子节点都返回Success
时,它才返回Success
,
当某个子节点返回Failure
时,顺序节点就会立刻返回Failure
,
1.2、新发口诀
顺序节点,从左到右,为真继续,全真才真,一假即假,一假即停。
类似于与
逻辑。
1.3、案例1:通知开饭了
我们使用Has Received Event
来监听开饭的事件,当它检测收到事件时才会返回Success
,此时顺序节点就会去执行下一个子节点,即干饭的逻辑,达到响应事件的功能,如下
其中Has Received Event
节点要设置监听的事件名称,定义一个字符串即可,
抛出事件有两种方式,一种是代码调用行为树的SendEvent
接口,如下
public void SendEvent(string name);
public void SendEvent<T>(string name, T arg1);
public void SendEvent<T, U>(string name, T arg1, U arg2);
public void SendEvent<T, U, V>(string name, T arg1, U arg2, V arg3);
示例:
// bt是BehaviorTree对象
bt.SendEvent("EAT_EVENT");
另外一种是使用SendEvent
节点,
设置要发送的事件名称即可,
1.4、案例2:吃几口饭吃一口菜
想象一下你吃饭的过程,扒拉吃几口饭,然后吃一口菜,我们可以用顺序节点来组织,
2、Parallel:并行节点
2.1、节点介绍
并行节点,顾名思义,它会并行执行所有的子节点,所有的子节点都返回Success
,并行节点才会返回Success
,
只要有一个子节点返回Failure
,并行节点就会立刻返回Failure
。如下图,我们可以看到,等10
秒和等20
秒的两个节点还没执行完就被停止了,因为中间那个等1
秒的节点执行完毕并返回了Failure
给并行节点,整个并行以Failure
结束,那些被终止的Wait
也会直接返回Failure
,
2.2、新发口诀
并行节点,并行执行,全真才真,一假即假,一假即停,被停即假。
2.3、案例:边跑步边听歌
比如我们边跑步边听歌,同时还要监听是否有下雨,有下雨的话要中断跑步,如下
这个例子我用到了Selector
节点,并且启用了Self
中断,看不懂的同学不要着急,下文我会讲。
3、Selector:选择节点
3.1、节点介绍
选择节点与顺序节点类似,也是从左到右执行,不同的是,当有子节点返回Success
,选择节点就会立刻返回Success
,不会执行下一个子节点
如果子节点返回Failure
,会就执行下一个子节点,其实很好理解,就是从左到右遍历选一个Success
,有Success
就返回Success
,否则执行下一个子节点,
如果所有子节点都返回Failure
,选择节点才返回Failure
,
3.2、新发口诀
选择节点,从左到右,一真即真,一真即停,全假才假。
3.3、案例:诈骗举报概率
比如收到诈骗短信后,有99%
的概率会执行举报,有1%
的概率会上当受骗,如下
4、Parallel Selector:并行选择节点
4.1、节点介绍
顾名思义,就是并行执行选择节点,有一个Success
就立即返回Success
,并且会终止其他子节点,被终止的子节点会返回Failure
,如下
所有子节点都返回Failure
时,并行选择节点才返回Failure
,
4.2、新发口诀
并行选择节点,并行执行,一真即真,一真即停,全假才假。
4.3、案例:考研和找工作
现在人生道路有两条路:考研、找工作,同时进行,其中一件事情成了,就吃一顿好的庆祝一下,如下
5、Priority Selector:优先级选择节点
5.1、节点介绍
这个节点和Selector
类似,Selector
的执行顺序是从左到右,而Priority Selector
会先检查子节点的优先级(priority
)进行排序,优先级高的优先执行。
问题来了,我们如何设置子节点的优先级呢?节点的优先级默认是0
,如果要修改优先级,需要重写Task
的GetPriority
方法,
这里我封装一个带优先级参数的日志节点吧:PriorityLog
,创建一个PriorityLog
脚本,代码如下
using UnityEngine;
namespace BehaviorDesigner.Runtime.Tasks
[TaskDescription("带优先级参数的日志节点")]
[TaskIcon("SkinColorLogIcon.png")]
public class PriorityLog : Action
[Tooltip("Text to output to the log")]
public SharedString text;
[Tooltip("Is this text an error?")]
public SharedBool logError;
[Tooltip("Should the time be included in the log message?")]
public SharedBool logTime;
[Tooltip("优先级")]
public SharedFloat priority;
/// <summary>
/// 重写获取优先级的方法
/// </summary>
/// <returns></returns>
public override float GetPriority()
return priority.Value;
public override TaskStatus OnUpdate()
// Log the text and return success
if (logError.Value)
Debug.LogError(logTime.Value ? string.Format("0: 1", Time.time, text) : text);
else
Debug.Log(logTime.Value ? string.Format("0: 1",Time.time, text) : text);
return TaskStatus.Success;
public override void OnReset()
// Reset the properties back to their original values
text = "";
logError = false;
logTime = false;
现在我们就可以在Tasks
列表中看到我们新封装的PriorityLog
节点了,
使用它连个简单的行为树,如下
我们可以选中节点,在Inspector
中设置节点的Priority
,
由于右边的节点优先级我们设置为了1
,比左边的节点优先级高,所以会优先执行右边的节点,因为右边的节点执行返回了Success
,此时Priority Selector
就会直接返回Success
,不会去执行左边的节点了,如下
5.2、新发口诀
优先级选择节点,优先级排序,一真即真,一真即停,全假才假。
5.3、案例:先吵架还是先道歉
我先给行为树创建一Foat
类型的变量:SorryPriority
,如下
以它作为道歉优先级变量,
接着制作如下的行为树,当按下A
键的时候,设置SorryPriority
变量,提高道歉的优先级,
其中Set Shared Float
节点的设置如下,让它去修改SorryPriority
变量为1
,
道歉节点的Priority
参数设置为SorryPriority
变量,如下
我们运行,如果我没有按下A
键,则道歉优先级是0
,2
秒情绪酝酿完毕后,会默认先执行吵架,
如果我在2
秒情绪酝酿期间按下了A
键,则会设置道歉优先级变量为1
,情绪酝酿完毕后就会优先执行道歉,就不会吵架了,
6、Random Selector:随机选择节点
6.1、节点介绍
这个很好理解,按选择节点的规则,只是不是从左到右执行,而是随机顺序执行。
如果有子节点返回Success
,则立即返回Success
,否则继续执行下一个节点,
只有所有的子节点都返回Failure
时,Random Selector
才返回Failure
,
6.2、新发口诀
随机选择节点,随机执行,一真即真,一真即停,全假才假。
6.3、案例:吃米饭、面条、饺子
中午吃什么好呢?随机选一个吧,好的,吃面条~
7、Random Sequence:随机顺序节点
7.1、节点介绍
这个也很好理解,就是顺序节点的规则,但不是从左到右执行,而是随机顺序执行。
当所有子节点都返回Success
时,它才返回Success
,
当某个子节点返回Failure
时,顺序节点就会立刻返回Failure
,
7.2、新发口诀
随机顺序节点,随机顺序,为真继续,全真才真,一假即假,一假即停。
7.3、案例:番茄炒蛋
我现在要做一道番茄炒蛋,有的人先炒番茄,有的人先炒鸡蛋,这里我们就可以使用随机顺序节点了,如果炒鸡蛋或者炒番茄失败,那么我们就吃不到番茄炒蛋了,只有两个都成功了,才能吃到番茄炒蛋,
8、Parallel Complete:并行竞争节点
8.1、节点介绍
这个节点其实就是并行执行所有的子节点,只要有一个子节点返回了结果,它就结束,并以这个子节点的结果为结果,
8.2、新发口诀
并行竞争节点,并行执行,最速之子,以子为果。
9、Selector Evaluator:评估选择节点
9.1、节点介绍
这个节点不是很好理解,它的逻辑是这样的,从左到右顺序执行,如果遇到子节点返回Success
,则立即结束,并返回Success
,否则继续执行下一个子节点。
有同学会问了,那它与Selector
节点有啥区别?
上面我用的是Log
节点,会立即返回结果,如果是用Wait
节点,则会有Running
状态,这个Selector Evaluator
节点是个固执的强迫症节点,它认为子节点应该给它返回Success
,如果子节点返回了Failure
,它会硬着头皮执行下一子节点,但是,一旦下一个子节点处于Running
状态,它就会像个渣男一样毫不犹豫地打断并返回第一个子节点,重新执行,只要有机会它就想要一个Success
结果,如果后续的子节点都是立即返回成功或失败,而没有Running
状态,则它会接受后续子节点的Success
,也就是说,它也不是非得第一个子节点不娶,后面的子节点只要立刻Success
,它就不打断去重新找第一个子节点了,相当地渣男啊!
看,第二任只要考虑一下(Running
状态),它就会立即打断回去执行初恋,
如果第二任立即答应了,它就会接受第二任的结果,
如果第二任直接给个Failure
,那么这个渣男会去骚扰路人甲,如果路人甲给它Success
,它就会爱上路人甲,
如果路人甲立即给它Failure
,这个渣男才会死心,
以上是关于游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)的主要内容,如果未能解决你的问题,请参考以下文章
游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)
游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)