Unity第三方插件Behavior Designer干货总结
发表于2018-03-07
Unity3D上,实现简单AI最好的工具,莫过于Behavior Designer了。也是本篇文章要和大家介绍的重点,毕竟Behavior Designer这个插件支持可视化编辑、支持可视化调试。虽然还没有UE4那个内置的行为树那么强大,但是现阶段来看,至少在Unity官方出行为树之前,是Unity平台的最优解决方案了。所以本篇文章就给大家总结了一些在使用Behavior Designer插件时的干货,分享给大家,希望能对大家有帮助。
1、创建行为树
选择一个你想要为之添加行为树的 GameObject
点击 Tools-》Behavior Designer-》Editor,打开行为树编辑器。界面图太大了,不全截图了。
点击加号可以建立一个新的行为树。
2、建立行为树
先说明一下,如果不了解行为树的原理,建议自己写一个行为树,不然很难真正透彻理解行为树的原理。
这里的讲解,是建立在你已经写过行为树,或者起码了解行为树的原理的基础上的。
2.1 右键单击编辑区,选择一个节点类型,即可添加节点。这个要是摸索不出来,我觉得,可以不用往下看了,所以,不截图了。
下面是一个未完成的行为树,但是拿来讲解应该足够了。
想必BombMan大家都玩过,没玩过可以下个FC模拟器玩玩。
我这个是想要实现一个PVP的炸弹人的AI部分。
这个图实现了部分功能,用人话翻译过来就是:
如果周围有可以炸掉的墙,并且我有炸掉,那么扔个(╯‵□′)╯炸弹!•••*~●(强大的输入法……)
否则如果我站在拐角处,那么选择一个方向,并且走到下一个格子。
看图我们需要确认以下几件事情。
- 不要指望内置的Tasks能满足你的需求,99%的情况下,你只能用到Composite,其他的Task你都得自己写。
- 掌握Task的粒度是关键,比如SelectDirection,本身就可以写成一个非常复杂的行为树。我以后会扩展它的。
- BehaviorTree能做的,硬编码一定都能实现,只不过,你要想调试,修改,维护你的硬编码AI,准备累死吧。
3、几个重要的选项
3.1 Start When Enable
建议不要选上,否则你很难控制并保证前期初始化工作,会在AI启动之前完成。
3.2 Paused When Disable
当GameObject被Disable掉时候,停掉AI,这个可以选上。
如果不选上,当你Disable掉GameObject的时候,行为树是被停止,而不是暂停。
选上他,当你再次激活GameObject的时候,行为树会被自动启动。
3.3 Restart When Complete
这个我选上了,不选上的话,你就得在Update或自己写事件驱动BTree,否则他执行一遍就不再执行了。
3.4 Reset Value When Restart
这个我也选上了,因为我的行为树,每次都会重新计算值,所以不需要继承原来的值。如果有需要利用上一次的计算结果,你可以不用点上它。
4、如何手动启动行为树
命名空间:
using BehaviorDesigner.Runtime;
调用代码:
BehaviorTree ai_tree =GetComponent<BehaviorTree>(); ai_tree.EnableBehavior();
5、如何自定义Task
绝大多数情况下,Behavior Designer给你的行为树是不够你用的(别听官方瞎BB,说什么一行代码也不写,就能做出游戏来,你自己玩玩可以,这么忽悠老板等着被炒鱿鱼吧,一行代码不写还要你程序猿干屁)。尤其是你想写一个策略游戏什么的,那点玩意根本不够看的。
自定义Task,要点也就那么几个。多数情况下,我们要派生自Conditional 和Action 这两种Task。
因为不幸中的万幸,Decoration和Composite节点,足够你用的。
就拿比较复杂的Action为例吧,Condition比较简单。不浪费笔墨了。
5.1 继承自Action的正确姿势。
包含这些,基本够了。
using UnityEngine; using System.Collections; using BehaviorDesigner.Runtime; using BehaviorDesigner.Runtime.Tasks;
5.2 必须要override的事件,只有两个,其他的是可选。
OnAwake
在这里你要获得一些对象的引用,比如对我来说,我想获得的是我自己写的AI_Controller的引用。
大约这样写:
ai_ctrl = ((GameObject)owner_ojbect.GetValue()).GetComponent<BA_AIController>();
注意,owner_ojbect是一个SharedGameObject对象,不要试图直接通过GameObject或者你自己的脚本获得引用。
因为他们可以在Inspector里面被看到,但是你把场景中的GameObject拖进去,会报错的。
OnUpdate
这个是核心函数。如果是Conditional节点,你需要实现你的比较函数。
True 返回 TaskStatus.Success;
False 返回 TaskStatus.Failure;
如果是延迟的Action,不能直接返回成功或者失败的,要返回TaskStatus.Running。并且把Instant属性勾掉。
public override TaskStatus OnUpdate() { if( inner_timer != null && inner_timer.paused == false) { inner_timer.Tick(Time.deltaTime); return TaskStatus.Running; } ai_ctrl.DropBomb(); return TaskStatus.Success; }
我这个写的比较草率,大意是,等播放放置炸弹的动画结束之后,逻辑上放置一个炸弹。
其他比较重要的可能需要override的方法:
OnStart和OnEnd分别在事件开始前,和事件结束后调用,如果是延迟事件,会等返回成功或失败后再调用OnEnd,返回Running的时候是不会调用它的。
比如我的移动到下个格子的Task是这样写的:
public override void OnStart() { move_to = ai_ctrl.player_object.last_stand_on.GetNeibhouor( ai_ctrl.current_direction ); } public override void OnEnd() { ai_ctrl.player_object.last_stand_on = move_to; } public override TaskStatus OnUpdate() { Vector3 distance = move_to.transform.position - ai_ctrl.player_object.transform.position; if (distance.magnitude < center_threshold) { return TaskStatus.Success; } ai_ctrl.player_object.transform.position = Vector3.MoveTowards(ai_ctrl.player_object.transform.position, move_to.transform.position, ai_ctrl.delta_speed); return TaskStatus.Running; }
OnStart中,选择目标格子
OnUpdate中,一帧一帧的移动角色。
OnEnd中,将角色定位到下一个格子中,这对策略游戏可能更重要。即时战略是没有格子的设定的。
6、Abort Type机制
这里顺便讲一下Instant这个选项,如果有Task是延迟事件,也就是需要运行一段时间的事件,那么他要被标记为非Instant,并且他的上层父节点,也要被标记为非Instant,否则他会在执行之后,立刻向上层报告,导致整个行为树重新计算。
给我的感觉,这是个短路机制。如果把整个行为树看成一个函数,那么abort机制相当于允许你在函数执行到最后之前就return掉。
None是不短路,直接运行到结束为止
Self是自己的子树完活之后,短路
LowerPriority,是比自己优先级低的子树完活之后,短路。
Both,是二者兼有之。
来自:http://blog.csdn.net/wanghaodiablo/article/details/52587364