转自: http://blog.csdn.net/qq_33747722/article/details/53539532
转自: https://my.oschina.net/acitiviti/blog/625799
在Behavior Designer的可视化节点实际都是由脚本语言编写出来的
自己手写一个类似于CanSeeObject、Seek等任务节点并不是一件难事.
下面我介绍CanSeeObject和Seek任务节点的代码生成,基本成功实现了原来节点的功能
LJLCanSeeObject代码如下:
using UnityEngine;
using System.Collections;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
#region 检测选中的游戏对象是否在视野内
/// <summary>
///CanSeeObject是在Conditional下的节点,因此自己手动写的一个类似于CanSeeObject的节点继承自Conditional,在这里命名为LJLCanSeeObject
/// </summary>
public class LJLCanSeeObject : Conditional
{
/// <summary>
/// 视野内检测的目标对象
/// </summary>
public Transform[] targets;
/// <summary>
/// 视野角度
/// </summary>
public float fieldOfViewAngle = 90;
/// <summary>
/// 视野距离(共享变量)
/// </summary>
public SharedFloat sharedViewDistance;
/// <summary>
/// 在视野内检测到的游戏对象位置(共享变量,在这里将检测到的游戏对象位置数据附给它)
/// </summary>
public SharedTransform target;
public override TaskStatus OnUpdate()
{
if (targets == null) return TaskStatus.Failure;
foreach (var target in targets)
{
float distance = (target.position - transform.position).magnitude;
float angle = Vector3.Angle(transform.forward, target.position - transform.position);
if (distance < sharedViewDistance.Value && angle < fieldOfViewAngle * 0.5f)
{
//将检测到的游戏对象位置数据附给共享变量
this.target.Value = target;
return TaskStatus.Success;
}
}
return TaskStatus.Failure;
}
}
#endregion
LJLSeek代码如下:
using UnityEngine;
using System.Collections;
using BehaviorDesigner.Runtime;
using BehaviorDesigner.Runtime.Tasks;
#region 控制游戏物体到达目标位置
public class MySeek : Action
{
/// <summary>
/// 行走速度(共享变量)
/// </summary>
public SharedFloat sharedSpeed;
/// <summary>
/// 目标位置(共享变量)
/// </summary>
public SharedTransform target;
/// <summary>
/// 到达目的地的距离(共享变量)
/// </summary>
public SharedFloat sharedArriveDistance = 0.1f;
private float sqrArriveDistance;
public override void OnStart()
{
sqrArriveDistance = sharedArriveDistance.Value*sharedArriveDistance.Value;
}
//当进入到这个任务的时候,会一直调用这个方法,直到任务结束
//返回一个 Success 或者 Failure 的状态,那么任务结束
//返回一个Running的状态,则这个方法会继续调用
public override TaskStatus OnUpdate()
{
if (target == null||target.Value==null)
{
return TaskStatus.Failure;
}
transform.LookAt(target.Value.position);//直接朝向目标位置
transform.position= Vector3.MoveTowards(transform.position, target.Value.position, sharedSpeed.Value*Time.deltaTime);
if ((target.Value.position - transform.position).sqrMagnitude < sqrArriveDistance)
{
//当距离目标位置的距离比较小时,认为到达了目标位置,直接return成功
return TaskStatus.Success;
}
return TaskStatus.Running;
}
}
#endregion
接下来Conditionals条件节点有个LJLCanSeeObject节点可以选择使用。
Actions行为节点有个LJLSeek节点可以选择使用。
行为树是一组任务的集合。任务拥有和unity MonoBehaviour 相似的API,所以他很容易的让你创建一个任务(创建任务将在后面章节详细讲解)。任务类有如下API:
// OnAwake is called once when the behavior tree is enabled. Think of it as a constructor
public virtual void OnAwake();
// OnStart is called immediately before execution. It is used to setup any variables that need to be reset from the previous run
public virtual void OnStart();
// OnUpdate runs the actual task
public virtual TaskStatus OnUpdate();
// OnEnd is called after execution on a success or failure.
public virtual void OnEnd();
// OnPause is called when the behavior is paused and resumed
public virtual void OnPause(bool paused);
// The priority select will need to know this tasks priority of running
public virtual float GetPriority();
// OnBehaviorComplete is called after the behavior tree finishes executing
public virtual void OnBehaviorComplete();
// OnReset is called by the inspector to reset the public properties
public virtual void OnReset();
// Allow OnDrawGizmos to be called from the tasks
public virtual void OnDrawGizmos();
// Keep a reference to the behavior that owns this task
public Behavior Owner;
任务有三个共有的属性:名称,注释,瞬时状态。瞬时状态是三个属性中一个不太容易理解的属性。在一个相同的循环周期中,一个任务返回成功或者失败后,任务将立刻运行下一个任务,先前的任务将处于等待状态。这种模式有利于减少行为树性能消耗。
下图显示了任务运行的流程: