行为树(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;
        }
    }
}
相关推荐
Robot_Nav9 天前
Nav2 行为树(Behavior Tree)技术详解
行为树·nav2·behavior tree
日月星辰cmc2 个月前
【开源】Groot2风格行为树可视化监控软件,能展示20个节点以上
机器人·开源软件·行为树
永恒星1 年前
行为树详解(6)——黑板模式
行为树·黑板模式
ue星空1 年前
UE5行为树浅析
人工智能·ai·ue5·行为树
永恒星1 年前
行为树详解(5)——事件驱动
行为树·轮询·事件驱动
永恒星1 年前
行为树详解(4)——节点参数配置化
行为树·参数配置
Flamesky1 年前
MMORPG技能管线设计经验总结
行为树·可视化·rpg·skill·mmo·战斗系统·技能编辑器·技能管线·mmorpg·arpg
蔗理苦2 年前
2024-07-22 Unity AI行为树1 —— 框架介绍
unity·c#·游戏引擎·行为树·游戏ai
大风吹~~~~~2 年前
行为树入门:BehaviorTree.CPP Groot2练习(叶子节点)(2)
行为树