简单版行为树(BehaviorTree)实现


简单版行为树(BehaviorTree)实现


2018/11/25更新:这篇博客只是对行为树的简单说明,不能按照下面说的这样来写代码,简直太蠢了.
1.代码是火箭式,写的时候要盯对括号,不然容易写歪
2.不易调试,加了打印信息代码又会很乱,打断点不知道具体是运行到了哪个节点,断点只能看到是各个节点的基类,后面维护起来也头疼.
3.因为不能调试,所以在测试的时候只能将节点分离出来进行测试来判断其行为是否正确.
4.最好还是有可视化的,可编辑的工具来做行为树...

接触到的第一个游戏AI制作方法就是行为树,我们游戏要做机器人,所以学以致用,实现了一个简单版的行为树.

行为树

行为树主要包含两类节点,一类是叶子节点:主要作用是实现功能;一类是复合节点:主要作用是控制.

行为树就是通过对不同的叶子节点进行组合后来达到AI控制效果的,每个叶子节点都会向其父节点报告执行状态,父节点再根据自己的逻辑改变自身状态,如此向上递归则可知整个行为树的状态.

叶子节点

- 动作节点:执行到此节点时,会执行一个动作,执行后返回执行成功.
- 条件节点:执行到此节点时,会进行条件判断,若条件判断成功返回执行成功否则返回执行失败.
- 等待节点:一般实现为执行到此节点时,会等待一段时间,若未超出等待时间则返回执行中,若超出等到时间则返回执行成功

复合节点

- 序列节点:执行到此节点时,会依次执行其子节点,若有子节点返回执行中,则其状态也为执行中,并等待该子节点执行完毕,若有子节点执行失败,其状态也为执行失败,若所有子节点执行成功,则其状态也为执行成功(相当于&).)
- 选择节点:执行到此节点时,会依次执行其子节点,若有子节点返回执行中,则其状态也为执行中,并等待该自己点执行完毕.若有子节点执行成功,其状态也为执行成功,若所有子节点执行失败,则其状态为执行失败(相当于|).
- 并行节点:

以上就是行为树的几种基本节点

具体代码


   public enum NodeType
    {
        Action
      , Condition
      , Wait
      , Sequence
      , Select
    }

    public enum NodeState
    {
        Ready
      , Running
      , Success
      , Failed
    }

    public class BehaviorTree
    {
        private readonly BtNode    _rootNode;
        private BtNode    _runingNode;
        public  NodeState State { get; private set; }

        public BehaviorTree(BtNode root)
        {
            _rootNode = root;
            State     = NodeState.Ready;
        }

        public NodeState Update()
        {
            State = _rootNode.OnVisit();
            return State;
        }

        public void Reset()
        {
            _rootNode.Reset();
        }
    }

    #region 节点定义

    public abstract class BtNode
    {
        public NodeType  Type  { get; private set; }
        public NodeState State { get; protected set; }

        protected BtNode(NodeType nodeType)
        {
            State = NodeState.Ready;
            Type  = nodeType;
        }

        public abstract NodeState OnVisit();

        public virtual void Reset()
        {
            State = NodeState.Ready;
        }
    }

    public abstract class BtCompostieNode : BtNode
    {
        protected List ChildNodes;

        protected BtCompostieNode(List nodes, NodeType nodeType) : base(nodeType)
        {
            ChildNodes = nodes ?? new List();
        }

        public virtual BtCompostieNode AddChild(BtNode node)
        {
            ChildNodes.Add(node);
            return this;
        }
    }

    #region 叶子节点

    // 动作节点
    public class ActionNode : BtNode
    {
        private readonly Action _action;

        public ActionNode(Action action) : base(NodeType.Action)
        {
            _action = action;
        }

        public override NodeState OnVisit()
        {
            _action?.Invoke();
            return NodeState.Success;
        }
    }

    // 条件节点
    public class ConditionNode : BtNode
    {
        private readonly Func _checkFunc;

        public ConditionNode(Func checkFunc) : base(NodeType.Condition)
        {
            _checkFunc = checkFunc;
        }

        public override NodeState OnVisit()
        {
            if (_checkFunc == null)
                return NodeState.Success;

            return _checkFunc.Invoke() ? NodeState.Success : NodeState.Failed;
        }
    }

    // 等待节点
    public class WaitNode : BtNode
    {
        private readonly int  _waitSec;
        private          long _startSec;

        public WaitNode(int waitSec) : base(NodeType.Wait)
        {
            _waitSec = waitSec;
        }

        public override NodeState OnVisit()
        {
            if (_startSec == 0)
            {
                _startSec = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
                State     = NodeState.Running;
                Console.WriteLine(DateTime.Now.Ticks / TimeSpan.TicksPerSecond);
            }

            // 还没有到时间返回running
            if (DateTime.Now < new DateTime((_waitSec + _startSec) * TimeSpan.TicksPerSecond))
                return State;

            Console.WriteLine(DateTime.Now.Ticks / TimeSpan.TicksPerSecond);
            State = NodeState.Success;
            return State;
        }

        public override void Reset()
        {
            base.Reset();
            _startSec = 0;
        }
    }

    #endregion

    #region 复合节点

    public class SequenceNode : BtCompostieNode
    {
        public SequenceNode(List nodes = null) : base(nodes, NodeType.Sequence)
        {
        }

        public override NodeState OnVisit()
        {
            foreach (var node in ChildNodes)
            {
                var result = node.OnVisit();
                if (result != NodeState.Success)
                    return result;
            }

            return NodeState.Success;
        }
    }

    public class SelectNode : BtCompostieNode
    {
        public SelectNode(List nodes = null) : base(nodes, NodeType.Select)
        {
        }

        public override NodeState OnVisit()
        {
            foreach (var node in ChildNodes)
            {
                var result = node.OnVisit();
                if (result != NodeState.Failed)
                    return result;
            }

            return NodeState.Failed;
        }
    }

测试代码(有条件还是别手写行为树了,挺费劲的)