行为树(Behavior Trees)

行为树(Behavior Trees)是一种在游戏开发中广泛使用的AI设计模式,主要用于描述AI的行为和决策过程,实现更加智能和自然的游戏AI。它由多个节点组成,每个节点代表一个行为或决策,按照特定的方式连接在一起,形成一个树状结构。

在行为树中,根节点是AI的起点,通过遍历子节点来决策AI的行为。节点有以下三种状态:成功(Success)、失败(Failure)和运行(Running)。前两个通知其父节点其操作是成功还是失败。第三种意味着尚未确定成功或失败,并且该节点仍在运行。下次树被选择时,该节点将再次被选择,此时它将再次有机会成功,失败或继续运行。

行为树的节点有以下几种主要原型:

  1. 组合控制节点(Composite):一种将多个子节点组合在一起的节点,用于实现复杂的行为和决策逻辑。主要包括次序节点(Sequencer)和选择节点(Selector)。次序节点并行执行多个子节点,直到所有子节点都返回成功或者任意一个子节点返回失败为止。选择节点按照顺序执行子节点,当某个子节点返回成功时,停止执行并返回成功。

  2. 修饰节点(Decorator):一种特殊的节点,它不执行具体的行为或决策,而是修饰其它节点的行为或决策。主要包括逆变节点(Inverter)和重复节点(Repeater)。逆变节点可以将子节点的结果倒转,比如子节点返回了失败,则这个修饰节点会向上返回成功,以此类推。重复节点重复执行其子节点指定的次数或者一直重复执行,直到其子节点返回成功或者失败。

  3. 叶节点(Leaf):树的最末端,就是这些AI实际上去做事情的命令。

行为树通过模拟树状结构来描述AI的行为和决策过程,使得AI的行为更加灵活、自然和智能。
代码实现

csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;

namespace BehaviourTree
{
    public class BehaviourTree : MonoBehaviour
    {
        private Node root = null;
        private Blackboard blackboard;

        public Node Root
        {
            get
            {
                return root;
            }
            set
            {
                root= value;
            }
        }

        void Start()
        {
            Init();
        }

        void Update()
        {
            if(root!=null&& gameObject!=null)
            {
                root.Evaluate(gameObject.transform,blackboard);
            }
        }

        protected virtual void Init()
        {
            blackboard = new Blackboard();
        }
        
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;

namespace BehaviourTree
{
    //行为树共享数据
    public class Blackboard
    {
        private Dictionary<string,object> Data;

        public Blackboard()
        {
            Data = new Dictionary<string, object>();
        }

        //获取数据
        public T Get<T>(string key)
        {
            if(Data.ContainsKey(key))
            {
                return (T)Data[key];
            }
            return default;
        }

        //添加数据
        public void Add<T>(string key,T value)
        {
            if(Data.ContainsKey(key))
            {
                Data[key] = value;
            }
            else
            {
                Data.Add(key,value);
            }
        }

        //删除数据
        public void Remove(string key)
        {
            if(Data.ContainsKey(key))
            {
                Data.Remove(key);
            }
            else
            {
                Debug.Log("数据不存在 "+key);
            }
        }
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;

namespace BehaviourTree
{
    //状态类型
    public enum Status
    {
        Running,    //运行中
        Failure,    // 失败
        Success,    //成功
    }

    //节点基类
    public abstract class Node
    {
        protected Node parent;
        protected Status status;

        Status Status
        {
            get
            {
                return status;
            }
            set
            {
                status = value;
            }
        }

        public Node()
        {
            
        }
        
        //声明节点状态返回方法
        public abstract Status Evaluate(Transform transform,Blackboard blackboard);
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;

namespace BehaviourTree
{
    //复合节点类
    public abstract class Composite : Node
    {
        //子节点列表
        protected List<Node> children = new List<Node>();
        protected int index;//子节点下标计数
        protected Composite(List<Node> children)
        {
            index = 0;
            foreach(var child in children) 
            {
                this.children.Add(child);
            }
        }
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;

namespace BehaviourTree
{
    //修饰器节点
    public abstract class Decorator : Node
    {
        //子节点列表
        protected List<Node> children;
        protected Decorator(List<Node> children)
        {
            children = new List<Node>(){};
            foreach(var child in children) 
            {
                this.children.Add(child);
            }
        }
    }

}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;

namespace BehaviourTree
{
    //选择节点,所有子节点都为失败则失败
    public class Selector : Composite
    {
        
        public Selector(List<Node> children) : base(children)
        {
            
        }

        //所有子节点都为失败则失败
        public override Status Evaluate(Transform transform, Blackboard blackboard)
        {
            if(index>=children.Count)
            {
                index = 0;
                return Status.Success;
            }
            var status = children[index].Evaluate(transform,blackboard);
            switch(status)
            {
                case Status.Success:
                    index = 0;
                    return Status.Success;
                case Status.Failure:
                    index+=1;
                    if(index>=children.Count)
                    {
                        index = 0;
                        return Status.Failure;
                    }
                    return Status.Running;
                case Status.Running:
                    return Status.Running;
                default:
                    return Status.Failure;
            }
        }
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;

namespace BehaviourTree
{
    //顺序节点,所有子节点成功才成功
    public class Sequencer : Composite
    {
        public Sequencer(List<Node> children) : base(children)
        {
            
        }

        //所有子节点成功才成功
        public override Status Evaluate(Transform transform, Blackboard blackboard)
        {
            if(index>=children.Count)
            {
                index = 0;
                return Status.Success;
            }
            var status = children[index].Evaluate(transform,blackboard);
            switch(status)
            {
                case Status.Success:
                    index+=1;
                    if(index>=children.Count)
                    {
                        index = 0;
                        return Status.Success;
                    }
                    return Status.Running;
                case Status.Failure:
                    return Status.Failure;
                case Status.Running:
                    return Status.Running;
                default:
                    return Status.Failure;
            }
        }
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;

namespace BehaviourTree
{
    //任务节点,这里会处理对象具体行为逻辑(叶节点)
    public abstract class Task : Node
    {
        
    }
}

简单使用

实现敌人在两点之间巡逻,人物靠近会变红温并停止移动,人物远离时继续巡逻

csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;
using System.ComponentModel.Design.Serialization;
using System.Linq;

namespace BehaviourTree
{
    public class EnemyBT : BehaviourTree
    {
        Vector3 aPos = new Vector3(4.07999992f,-2.21000004f,-2);
        Vector3 bPos = new Vector3(4.07999992f,1.65999997f,-2)
        protected override void Init()
        {
            //调用基类初始化
            base.Init();
            //创建变红节点
            TurnRed turnRed = new TurnRed();
            //创建巡逻节点
            Patrol patrol = new Patrol(aPos,bPos);
            //创建查找节点
            FindObject findObject = new FindObject();
            //创建侦查节点子节点序列
            Node[] spyChildren = {findObject,turnRed};
            //创建顺序节点用于执行侦查行为
            EnemySequencer enemySequencer = new EnemySequencer(spyChildren.ToList());
            //创建根节点子节点序列
            Node[] selectorChildren = {enemySequencer,patrol};
            //创建选择节点用于处理侦查和巡逻行为
            EnemySelector enemySelector = new EnemySelector(selectorChildren.ToList());
            //将选择节点设置为根节点
            Root = enemySelector;
            
        }
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;

namespace BehaviourTree
{
    public class EnemySelector : Selector
    {
        public EnemySelector(List<Node> children) : base(children)
        {
            
        }
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;

namespace BehaviourTree
{
    public class EnemySequencer : Sequencer
    {
        public EnemySequencer(List<Node> children) : base(children)
        {
    
        }
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;
using DG.Tweening;

namespace BehaviourTree
{
    public class FindObject : Task
    {
        float radius = 5f;
        int layer = 512;
        public override Status Evaluate(Transform transform, Blackboard blackboard)
        {
            Collider2D obj = Physics2D.OverlapCircle(transform.position,radius,layer);
            if(obj!=null)
            {
                return Status.Success;   
            }
            transform.gameObject.GetComponent<SpriteRenderer>().DOColor(Color.white,1);
            return Status.Failure;
        }
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;
using DG.Tweening;

namespace BehaviourTree
{
    //在aPoint和bPoint之间来回移动
    public class Patrol : Task
    {
        Vector3 aPoint;
        Vector3 bPoint;
        float speed = 4f;
        Vector3 target;
        public Patrol(Vector3 aPos,Vector3 bPos)
        {
            aPoint = aPos;
            bPoint = bPos;
            target = aPoint;
        }
        public override Status Evaluate(Transform transform, Blackboard blackboard)
        {
            if(Vector2.Distance(transform.position,target)<0.1f)
            {
                if(target == aPoint)
                {
                    target = bPoint;
                }
                else
                {
                    target = aPoint;
                }
            }
            else
            {
                transform.position = Vector2.MoveTowards(transform.position,target,Time.deltaTime*speed);
            }
            return Status.Success;
        }
    }
}
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BehaviourTree;
using DG.Tweening;

namespace BehaviourTree
{
    //变红
    public class TurnRed : Task
    {
        Vector3 bPoint;
        public override Status Evaluate(Transform transform, Blackboard blackboard)
        {
            transform.gameObject.GetComponent<SpriteRenderer>().DOColor(Color.red,1);
            return Status.Success;
        }
    }
}
相关推荐
永恒星8 天前
行为树详解(4)——节点参数配置化
行为树·参数配置
Flamesky1 个月前
MMORPG技能管线设计经验总结
行为树·可视化·rpg·skill·mmo·战斗系统·技能编辑器·技能管线·mmorpg·arpg
蔗理苦5 个月前
2024-07-22 Unity AI行为树1 —— 框架介绍
unity·c#·游戏引擎·行为树·游戏ai
大风吹~~~~~10 个月前
行为树入门:BehaviorTree.CPP Groot2练习(叶子节点)(2)
行为树
姚家湾1 年前
行为树(BEHAVIOR TREES)及其工业应用
行为树