本系列文章部分内容参考自 https://zhuanlan.zhihu.com/p/19890016?columnSlug=indiegamepixel
感谢原作者的知识分享
行为树包括 :
基础节点:BaseNode
前置条件节点:PreconditionNode
顺序节点:SequenceNode
并行节点:ParallelNode ( 当一个节点不通过就不执行 )
并行节点2:ParallelFlexibleNode ( 当所有节点不通过才不执行 )
选择节点: PrioritySelectorNode
逻辑节点:ActionNode
数据提供者: DataBase
行为树: BTree
实现:
基础节点:BaseNode
namespace BehaviorTree
{
public abstract class BaseNode
{
#region 字段
public string name;
protected List children; // 子节点
public PreconditionNode precondition; // 放行条件
public DataBase database; // 数据提供者
public float intervalTime = 0; // 冷却时间
private float intervalTimer = 0;
public bool activated; // 是否激活过了
#endregion
#region 属性
public List Children { get { return children; } }
//节点可行判断
public bool Evaluate { get { return activated && CheckTimer() && (precondition == null || precondition.Check()) && DoEvaluate(); } }
#endregion
public BaseNode() : this(null) { }
public BaseNode(PreconditionNode precondition) { this.precondition = precondition; }
#region 接口
///
/// 激活
///
///
public virtual void Activate(DataBase database)
{
if (activated)
return;
this.database = database;
if (precondition != null)
precondition.Activate(database);
if (children != null)
foreach (var n in children)
n.Activate(database);
activated = true;
}
///
/// 个性化可行
///
///
protected virtual bool DoEvaluate() { return true; }
///
/// 执行
///
///
public virtual NodeResult Tick() { return NodeResult.Ended; }
public virtual void Clear() { }
public virtual void AddChild(BaseNode node)
{
if (children == null)
children = new List();
if (node != null)
children.Add(node);
}
public virtual void RemoveChild(BaseNode node)
{
if (children != null && node != null && children.Contains(node))
children.Remove(node);
}
///
/// 是否冷却完成
///
///
private bool CheckTimer()
{
//当前时间-刚才的时间=过去的时间
if (Time.time - intervalTimer > intervalTime)
{
intervalTimer = Time.time;
return true;
}
return false;
}
#endregion
}
///
/// 节点结果
///
public enum NodeResult{
Ended = 1,
Running = 2
}
}
namespace BehaviorTree
{
public abstract class PreconditionNode : BaseNode
{
public PreconditionNode() : base(null) { }
///
/// 是否通过条件
///
///
public abstract bool Check();
public override NodeResult Tick()
{
if (Check())
return NodeResult.Ended;
else
return NodeResult.Running;
}
}
}
namespace BehaviorTree
{
///
/// 数据提供者
///
public class DataBase : MonoBehaviour
{
private List
namespace BehaviorTree
{
public class ActionNode : BaseNode
{
private ActionNodeState _status = ActionNodeState.Ready;
public ActionNode(PreconditionNode precondition = null) : base(precondition) { }
///
/// 进入节点触发
///
protected virtual void Enter () {
}
///
/// 退出节点触发
///
protected virtual void Exit () {
}
///
/// 节点执行中
///
///
protected virtual NodeResult Execute()
{
return NodeResult.Running;
}
public override void Clear()
{
if (_status != ActionNodeState.Ready)
{
Exit();
_status = ActionNodeState.Ready;
}
}
public override NodeResult Tick()
{
NodeResult result = NodeResult.Ended;
//进入
if (_status == ActionNodeState.Ready)
{
Enter();
_status = ActionNodeState.Running;
}
//运行
if (_status == ActionNodeState.Running)
{
result = Execute();
//结束
if (result != NodeResult.Running)
{
Exit();
_status = ActionNodeState.Ready;
}
}
return result;
}
public override void AddChild(BaseNode node)
{
}
public override void RemoveChild(BaseNode node)
{
}
}
public enum ActionNodeState
{
Ready = 1,
Running = 2
}
}
namespace BehaviorTree
{
public class BTree : MonoBehaviour
{
#region 字段
protected BaseNode _root = null; // 根节点
[HideInInspector]
public DataBase database; // 数据提供者
[HideInInspector]
public bool isRunning = true;
public const string RESET = "Rest"; // 重置宏
private static int _resetId;
#endregion
#region Unity回调
void Awake()
{
Init();
//激活根节点
_root.Activate(database);
}
void Update()
{
if (!isRunning) return;
//判断是否需要重置
if (database.GetData(RESET))
{
Reset();
database.SetData(RESET, false);
}
//执行行为树
if (_root.Evaluate)
{
_root.Tick();
}
}
void OnDestroy()
{
if (_root != null)
{
_root.Clear();
}
}
#endregion
#region 接口
///
/// 初始化数据提供者
///
protected virtual void Init()
{
database = GetComponent();
if (database == null)
{
database = gameObject.AddComponent();
}
_resetId = database.GetDataId(RESET);
database.SetData(_resetId, false);
}
protected void Reset()
{
if (_root != null)
{
_root.Clear();
}
}
#endregion
}
}