unity行为树制作AI简单例子(2)
继续昨天的工程,给Monster添加一个空物体命名为AI,在AI添加脚本BehaviorTree,然后就可以打开行为树编辑器进行编辑了
先写好自定义的节点脚本,下面是一个寻找漫游点的行为节点脚本
using UnityEngine; using BehaviorDesigner.Runtime.Tasks; using BehaviorDesigner.Runtime; [TaskCategory("MyActions")] [TaskDescription("计算出更新的路径")] public class ChooseWanderPosition : Action { [BehaviorDesigner.Runtime.Tasks.Tooltip("最长距离")] public float wanderDistance; [BehaviorDesigner.Runtime.Tasks.Tooltip("存放目标地的变量")] public SharedVector3 destination; [BehaviorDesigner.Runtime.Tasks.Tooltip("自己")] public SharedGameObject monster; public override TaskStatus OnUpdate(){ Vector3 direction = new Vector3(Random.Range(-1f, 1f), 0f, UnityEngine.Random.Range(-1f, 1f)); direction *= wanderDistance; destination.Value = monster.Value.transform.position + direction; return TaskStatus.Success; } }
[TaskCategory("MyActions")]
指定文件的目录为MyActions,我是在Actions下新建了一个MyActions的文件夹,所以指定目录为MyActions
其实也可以不指定,但是添加节点时就会显得杂乱
[TaskDescription("计算出更新的路径")]
这可以提示脚本的功能
public的属性将会显示在面板上
既可使用int float GameObject这种unity的类型,但缺点在于无法和其他脚本共享变量
Shared xxx类型是插件提供的,他在整个行为树中都能使用
使用Shared xxx类型前,必须在Variables先定义变量
之后点击该按钮切换后就能选择全局的变量了
最后设置如下,该节点就完成了
[BehaviorDesigner.Runtime.Tasks.Tooltip("xxx")]是面板上变量的解释,移动到面板变量上会显示
作为行为节点必须继承自Action并实现OnUpdate
OnUpdate()是节点执行时要运行的代码
接下来贴出其他要用到的行为节点
using UnityEngine; using BehaviorDesigner.Runtime.Tasks; using BehaviorDesigner.Runtime; [TaskCategory("MyActions")] [TaskDescription("转向玩家")] public class FaceToPlayer : Action { [BehaviorDesigner.Runtime.Tasks.Tooltip("朝向的目标玩家")] public SharedGameObject player; [BehaviorDesigner.Runtime.Tasks.Tooltip("自己")] public SharedGameObject monster; public override TaskStatus OnUpdate(){ Vector3 targetDir = player.Value.transform.position - monster.Value.transform.position; monster.Value.transform.rotation = Quaternion.LookRotation(targetDir); return TaskStatus.Success; } }
using UnityEngine; using BehaviorDesigner.Runtime.Tasks; using BehaviorDesigner.Runtime; [TaskCategory("MyActions")] [TaskDescription("移动至漫游点")] public class MoveToWanderPoint : Action { [BehaviorDesigner.Runtime.Tasks.Tooltip("移动的速度")] public float speed = 3; [BehaviorDesigner.Runtime.Tasks.Tooltip("移动的目标点")] public SharedVector3 position; [BehaviorDesigner.Runtime.Tasks.Tooltip("自己")] public SharedGameObject monster; public override TaskStatus OnUpdate(){ monster.Value.GetComponent<NavMeshAgent>().Move(position.Value); return TaskStatus.Success; } }
using UnityEngine; using BehaviorDesigner.Runtime.Tasks; using BehaviorDesigner.Runtime; [TaskCategory("MyActions")] [TaskDescription("移动至玩家")] public class MoveToWanderPoint : Action { [BehaviorDesigner.Runtime.Tasks.Tooltip("移动的速度")] public float speed = 3; [BehaviorDesigner.Runtime.Tasks.Tooltip("移动的目标玩家")] public SharedGameObject player; [BehaviorDesigner.Runtime.Tasks.Tooltip("自己")] public SharedGameObject monster; public override TaskStatus OnUpdate(){ monster.Value.GetComponent<NavMeshAgent>().Move(player.Value.transform.position); return TaskStatus.Success; } }
因为移动调用了NavMeshAgent,所以我们先烘焙下场景并给Monster添加NavMeshAgent控件
using UnityEngine; using BehaviorDesigner.Runtime.Tasks; using BehaviorDesigner.Runtime; [TaskCategory("MyActions")] [TaskDescription("设置怪物的基本属性")] public class SetInfo : Action { [BehaviorDesigner.Runtime.Tasks.Tooltip("玩家")] public SharedGameObject player; [BehaviorDesigner.Runtime.Tasks.Tooltip("自己")] public SharedGameObject monster; public override TaskStatus OnUpdate(){ monster.Value = transform.parent.gameObject; player.Value = GameObject.FindWithTag("Player"); return TaskStatus.Success; } }
我们还需要自己写一个判断节点来判定玩家和怪物的距离,还需一个判断节点判断物体是否移动
using UnityEngine; using BehaviorDesigner.Runtime.Tasks; using BehaviorDesigner.Runtime; [TaskCategory("MyConditionals")] [TaskDescription("判断范围内是否有玩家")] public class PlayerInRange : Conditional { [BehaviorDesigner.Runtime.Tasks.Tooltip("距离")] public float ranage; [BehaviorDesigner.Runtime.Tasks.Tooltip("判断距离的玩家")] public SharedGameObject player; [BehaviorDesigner.Runtime.Tasks.Tooltip("自己")] public SharedGameObject monster; public override TaskStatus OnUpdate(){ if(Vector3.Distance(player.Value.transform.position, monster.Value.transform.position) > ranage){ return TaskStatus.Failure; } return TaskStatus.Success; } }
using UnityEngine; using BehaviorDesigner.Runtime.Tasks; using BehaviorDesigner.Runtime; [TaskCategory("MyConditionals")] [TaskDescription("判断对象是否移动")] public class NotMove : Conditional { [BehaviorDesigner.Runtime.Tasks.Tooltip("上一次的位置")] public Vector3 lastPosition; [BehaviorDesigner.Runtime.Tasks.Tooltip("判断的对象")] public SharedGameObject obj; public override TaskStatus OnUpdate(){ if(obj.Value.transform.position == lastPosition){ return TaskStatus.Success; } lastPosition = obj.Value.transform.position; return TaskStatus.Failure; } }
依靠这些节点就可以拼出一颗行为树,一运行就可以看到一个简单的AI在运行,在攻击范围内它会攻击,在追击范围内它会追击敌人直到可以攻击,否则漫游
红圈位置可以改变节点颜色,更容易观察行为树结构
一些细节方面可以观看Demo
http://git.oschina.net/yejinqi/BehaviorTreeDemo
如果的事:http://y.qq.com/#type=song&mid=0040ZKt52jxJ6Y&play=1