Behavior Creator 行为树可视化编辑器
- 这是一款为 CocosCreator 深度定制的行为树可视化编辑器插件
- 已实现
Selector
、Sequence
、Parallel
三种组合节点 - 已实现
Conditional
、Decorator
两种装饰器 - 已实现
Service
服务类型 - 已实现
Task
任务类型 - 支持自定义节点注册到行为树编辑器
- 支持类属性差异化更新
- 支持节点生命周期
onEnable
、onUpdate
、onDisable
等通用事件委托
注意
- 仅支持在 CocosCreator v3.3.0 及以上版本中使用
效果预览
快速开始
-
在
CocosCreator
编辑器菜单扩展
-->商城
中搜索BehaviorCreator
即可找到该插件,购买后下载到本地 -
导入插件
oreo-behavior-creator
并启用 -
在
资源管理器
面板中看到oreo-behavior-creator
运行时
已载入,则表示插件导入成功 -
注意,因插件需要导入运行时脚本,在
CocosCreator
编辑器已打开状态下可能会出现以下错误,此时需要重启编辑器
插件入口
-
行为树编辑器是由
json
数据驱动的,其入口已集成到BehaviorTree
组件的属性检查器
面板,可通过以下方式启动-
新建空节点,在节点上挂载
BehaviorTree
组件 -
新建内容为空
{}
的json
资源,并关联到BehaviorTree
的JsonAsset
属性。点击Edit Behavior
即可打开行为树编辑器
-
加载示例
-
在运行时目录
extensions\oreo-behavior-creator\runtime\examples\data
中提供了一些简单示例,可以通过Load
菜单加载到行为树编辑区,Save
保存后下一步运行查看效果。
运行效果
-
BehaviorTree
组件默认是没有启用 LogTaskChanges 属性的,我们先勾选以便在控制台输出 log。注:当行为树某次执行结果不是 Running 状态时,本次运行结束。如果需要重新开始,需要勾选
RestartWhenComplete
。
示例分析
-
我们通过分解行为树并编写代码逻辑,一步步实现以下效果
巡逻行为分析
-
我们设定一次巡逻主要包括以下逻辑
-
在指定区域目标点之间来回移动
通用
Task
任务节点 -
到达指定点后原地休息等待一段时间
Wait
任务节点
-
-
由于上述是一个连续有序的行为,因此需要将它们挂载到
Sequence
父节点Sequence
序列节点 -
整体巡逻行为如图所示
场景搭建
-
如图所示,我们在场景中添加了一个
AIEnemy
敌人 AI 以及三个巡逻目标点
代码逻辑
-
随机移动
在巡逻行为树结构中,
随机移动
节点是一个通用Task
任务节点,需要我们定制onUpdate
任务更新逻辑。新建
AIEnemy.ts
组件类并实现找点随机移动逻辑,挂载到AIEnemy
场景节点上。主要逻辑包含两点:1、到达目标点时返回
Success
表示成功,由父节点Sequence
顺序执行下一个子节点的行为,即原地休息
2、未到达目标点时返回
Running
表示任务还在持续,由父节点Sequence
下一次 tick 继续执行具体实现如下:
onRandomMove(){ this._playAnim("run", 1); //这里为了方便,只是按点索引顺序移动 let point = this.patrolPoints[this._index]; //自身点位置 pos1.set(this.target.position); //目标点位置 pos2.set(point.position); //朝向 this.target.lookAt(pos2); //目标点达标与否阈值判断 if(Vec3.distance(pos1, pos2) < 0.1){ this._index++; if(this._index>=this.patrolPoints.length){ this._index = 0; } this._playAnim("idle"); //到达目标点时返回成功,由父节点 `Sequence` 顺序执行下一个子节点的行为,即 `原地休息` return bt.BehaviorStatus.Success; } else{ let pos = this.lerp(out, pos1, pos2, 0.02); this.target.setPosition(pos); //未到达目标点时返回 Running ,由父节点 `Sequence` 下一次 tick 继续执行 return bt.BehaviorStatus.Running; } }
-
原地休息
这是一个内置
Wait
任务节点,主要作用就是在指定时间内任务直接返回Running
状态,表示任务还在持续,时间到达后返回Success
表示任务完成。Wait
任务有一个duration
属性,表示任务持续时间。这是一个SharedVariable
共享变量属性,与Blackboard
黑板变量关联。
逻辑绑定
-
我们先来指定
原地休息
时间新建一个
SharedNumber
数字类型的黑板共享变量,值设定为 2,并指定给duration
属性,表示原地休息 2 秒 -
绑定
随机移动
任务的onUpdate
委托逻辑。如图,将
onUpdate
逻辑绑定到AIEnemy
组件的onRandomMove
方法Delegate
委托逻辑绑定与CocosCreator
内置按钮事件绑定过程相似
运行效果
-
首次运行效果如下
-
我们发现AI 移动到指定目标点且原地休息结束后,并没有继续移动到下一个目标点。这是因为,对于本次行为来说,
Wait
执行结束并返回Success
状态,表示行为树已经全部执行完毕。如果需要重新开始,只需勾选BehaviorTree
组件上的RestartWhenComplete
。 -
最终效果如下
追逐行为分析
-
我们设定一次追逐主要包括以下逻辑
-
当玩家出现在
AIEnemy
的感应区域内时,AIEnemy
立即移动并靠近玩家通用
Task
任务节点 -
当玩家被追逐达到受击范围时,
AIEnemy
发动一次攻击。我们设定一次攻击包含实际攻击和技能CD两个任务。Sequence
序列节点,通用Task
任务节点、Wait
任务节点
-
-
由于
靠近
和一次攻击
是区分条件执行(不在受击范围就靠近,在受在范围就攻击),即一次只会执行一个分支,因此需要将它们挂载到Selector
父节点Selector
序列节点 -
此时
追逐玩家
行为如图所示
场景搭建
-
如图所示,我们在上一个场景的基础上添加了一个
AIPlayer
节点表示当前玩家
代码逻辑
-
靠近
靠近
节点也是一个通用Task
任务节点,需要我们定制onUpdate
任务更新逻辑。更新
AIEnemy.ts
组件类并实现相关逻辑。主要逻辑包含:1、判断玩家是否在受击范围内
2、不在范围内则靠近
3、在范围内则攻击
范围判断:
canAttack(){ //当前位置 pos1.set(this.target.position); //玩家位置 pos2.set(this.player.position); //受击范围判断 if(Vec3.distance(pos1, pos2) < 1){ return bt.BehaviorStatus.Success; } return bt.BehaviorStatus.Failure; }
移动到攻击范围
onMoveToAttack(){ if(this.canAttack()==bt.BehaviorStatus.Success){ return bt.BehaviorStatus.Failure; } else{ pos1.set(this.target.position); pos2.set(this.player.position); this.target.lookAt(pos2); let pos = this.lerp(out, pos1, pos2, 0.03); this.target.setPosition(pos); this._playAnim("runHit"); return bt.BehaviorStatus.Running; } }
发动攻击
onAttack(){ this._playAnim("attackLeft"); //一次攻击结束后等待技能冷却,所以在攻击动作结束后播放防御动画 'fightIdel' this.anim.once(AnimationComponent.EventType.LASTFRAME, ()=>{ this._playAnim("fightIdle"); }) //主角受击 this.aiPlayer.beAttacked(); return bt.BehaviorStatus.Success; }
技能CD
这也是一个内置
Wait
任务节点,与上一节中巡逻
行为的原地休息
节点类似,只需指定持续时间duration
即可
逻辑绑定
-
分别绑定
靠近
和攻击
任务的onUpdate
委托逻辑。 -
本次运行效果如下
我们看到,AI 启动后立即向玩家靠近,这不太科学。我们给 AI 添加一个感应区域作为 AI 的视野,当玩家出现在感应区域范围内时 AI 才奔向玩家。
-
我们使用内置的
Conditional
条件元素来实现条件判断 -
视野判断代码实现
onHasSight(){ //当前位置 pos1.set(this.target.position); //玩家位置 pos2.set(this.player.position); //视野范围 if(Vec3.distance(pos1, pos2) < 8){ return bt.BehaviorStatus.Success; } return bt.BehaviorStatus.Failure; }
-
绑定
onUpdate
逻辑
-
运行效果
-
增加视野判断后,运行效果如下
圆形区域为 AI 视野,当玩家进入视野时,AI 开始追逐,当追逐到玩家受击范围时,展开攻击。
下一步
加入
- BehaviorCreator QQ交流群:
- 659064495
说明
- 压缩包内包含完整运行时源码以及示例项目工程,详见包内 Readme.md 文档