简单版行为树(BehaviorTree)实现

Posted fallever

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单版行为树(BehaviorTree)实现相关的知识,希望对你有一定的参考价值。

简单版行为树(BehaviorTree)实现


接触到的第一个游戏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<BtNode> ChildNodes;

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

        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<bool> _checkFunc;

        public ConditionNode(Func<bool> 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<BtNode> 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<BtNode> 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;
        }
    }

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

技术分享图片

以上是关于简单版行为树(BehaviorTree)实现的主要内容,如果未能解决你的问题,请参考以下文章

关于BehaviorTree.CPP库

游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)

游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)

游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)

游戏开发教程BehaviorDesigner插件制作AI行为树(Unity | 保姆级教程 | 动态图演示 | Unity2021最新版)

使用行为树(Behavior Tree)实现游戏AI