本系列文章部分内容参考自 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<BaseNode> children; // 子节点
public PreconditionNode precondition; // 放行条件
public DataBase database; // 数据提供者
public float intervalTime = 0; // 冷却时间
private float intervalTimer = 0;
public bool activated; // 是否激活过了
#endregion
#region 属性
public List<BaseNode> 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 接口
/// <summary>
/// 激活
/// </summary>
/// <param name="database"></param>
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;
}
/// <summary>
/// 个性化可行
/// </summary>
/// <returns></returns>
protected virtual bool DoEvaluate() { return true; }
/// <summary>
/// 执行
/// </summary>
/// <returns></returns>
public virtual NodeResult Tick() { return NodeResult.Ended; }
public virtual void Clear() { }
public virtual void AddChild(BaseNode node)
{
if (children == null)
children = new List<BaseNode>();
if (node != null)
children.Add(node);
}
public virtual void RemoveChild(BaseNode node)
{
if (children != null && node != null && children.Contains(node))
children.Remove(node);
}
/// <summary>
/// 是否冷却完成
/// </summary>
/// <returns></returns>
private bool CheckTimer()
{
//当前时间-刚才的时间=过去的时间
if (Time.time - intervalTimer > intervalTime)
{
intervalTimer = Time.time;
return true;
}
return false;
}
#endregion
}
/// <summary>
/// 节点结果
/// </summary>
public enum NodeResult{
Ended = 1,
Running = 2
}
}
namespace BehaviorTree
{
public abstract class PreconditionNode : BaseNode
{
public PreconditionNode() : base(null) { }
/// <summary>
/// 是否通过条件
/// </summary>
/// <returns></returns>
public abstract bool Check();
public override NodeResult Tick()
{
if (Check())
return NodeResult.Ended;
else
return NodeResult.Running;
}
}
}
namespace BehaviorTree
{
/// <summary>
/// 数据提供者
/// </summary>
public class DataBase : MonoBehaviour
{
private List<object> _database = new List<object>(); // 数据
private List<string> _dataNames = new List<string>(); // 数据名
#region 接口
/// <summary>
/// 根据数据名取得数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dataName"></param>
/// <returns></returns>
public T GetData<T>(string dataName)
{
int dataId = IndexOfDataId(dataName);
if (dataId == -1) Debug.LogError("Database: Data for " + dataName + " does not exist!");
return (T)_database[dataId];
}
/// <summary>
/// 根据ID取得数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dataId"></param>
/// <returns></returns>
public T GetData<T>(int dataId)
{
return (T)_database[dataId];
}
/// <summary>
/// 根据数据名设置数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dataName"></param>
/// <param name="data"></param>
public void SetData<T>(string dataName, T data)
{
int dataId = GetDataId(dataName);
_database[dataId] = (object)data;
}
/// <summary>
/// 根据数据ID设置数据
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dataId"></param>
/// <param name="data"></param>
public void SetData<T>(int dataId, T data)
{
_database[dataId] = (object)data;
}
/// <summary>
/// 根据数据名取得数据ID
/// </summary>
/// <param name="dataName"></param>
/// <returns></returns>
public int GetDataId(string dataName)
{
int dataId = IndexOfDataId(dataName);
if (dataId == -1)
{
_dataNames.Add(dataName);
_database.Add(null);
dataId = _dataNames.Count - 1;
}
return dataId;
}
private int IndexOfDataId(string dataName)
{
for (int i = 0; i < _dataNames.Count; i++)
{
if (_dataNames[i].Equals(dataName)) return i;
}
return -1;
}
public bool ContainsData(string dataName)
{
return IndexOfDataId(dataName) != -1;
}
#endregion
}
}
namespace BehaviorTree
{
public class ActionNode : BaseNode
{
private ActionNodeState _status = ActionNodeState.Ready;
public ActionNode(PreconditionNode precondition = null) : base(precondition) { }
/// <summary>
/// 进入节点触发
/// </summary>
protected virtual void Enter () {
}
/// <summary>
/// 退出节点触发
/// </summary>
protected virtual void Exit () {
}
/// <summary>
/// 节点执行中
/// </summary>
/// <returns></returns>
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
}
}
行为树: BTree
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<bool>(RESET))
{
Reset();
database.SetData<bool>(RESET, false);
}
//执行行为树
if (_root.Evaluate)
{
_root.Tick();
}
}
void OnDestroy()
{
if (_root != null)
{
_root.Clear();
}
}
#endregion
#region 接口
/// <summary>
/// 初始化数据提供者
/// </summary>
protected virtual void Init()
{
database = GetComponent<DataBase>();
if (database == null)
{
database = gameObject.AddComponent<DataBase>();
}
_resetId = database.GetDataId(RESET);
database.SetData<bool>(_resetId, false);
}
protected void Reset()
{
if (_root != null)
{
_root.Clear();
}
}
#endregion
}
}