Unity中状态机与行为树的简单实现

前言

状态机与行为树是两大热门AI实现方式,这里简单贴出我的实现,仅作为参考,简单讲解原理,多的就不做解释,其他的AI实现方式可以使用GOAP或ML_Agent

状态机

状态机由三部分组成:状态、转换、条件。

状态表示对象当前的行为模式,如待机、追击、攻击。每个状态有进入、持续、退出三个阶段,用于处理动画切换、物理参数变更等副作用。

转换定义状态间的迁移路径。不同于硬编码的if-else,转换与状态解耦,形成有向图结构。运行期从当前状态出发,遍历所有出边,检查条件决定是否迁移。

条件是转换的守卫,返回布尔值。将条件抽象为独立对象,可实现复用(如"距离小于X"被多个转换共用)与组合(与、或、非)。

数据驱动体现在:状态机资产(SO)描述图结构,运行实例持有当前状态与上下文数据。同一资产可被多个敌人共享,各自独立推进。

复制代码
//状态
public abstract class BaseState : ScriptableObject 
{
    public StateType Type;
    public virtual void Enter(BaseStateValue value){}
    public virtual void Exit(BaseStateValue value){}
    public virtual void OnUpdate(BaseStateValue value) {}
} 
//条件
public abstract class BaseCondition :ScriptableObject 
 {
     public abstract bool CanTranslate(BaseStateValue value);
 } 
 //状态机
 public abstract class BaseStateMachine : MonoBehaviour 
 {
     [SerializeField]
     protected BaseStateMachineAsset _stateMachineAsset;
     protected BaseState _currentState;
     public BaseStateValue _currentStateValue;
     protected virtual void Start()
     {
         CreateStateValue();
         Initialize();
     }
     protected void Update()
     {
         _currentState.OnUpdate(_currentStateValue);
         var list = _stateMachineAsset.GetTranslations(_currentState.Type);
         if (list == null) return;
         
         foreach (var t in list)
         {
             if(t.Condition.CanTranslate(_currentStateValue))
             {
                 _currentState.Exit(_currentStateValue);
                 _currentState = t.To;
                 _currentState.Enter(_currentStateValue);
                 break;
             }
         }
     }
     protected void FixedUpdate()
     {
         if(_currentState is IPhysicsState physics)
         {
             physics.OnFixedUpdate(_currentStateValue);
         }
     }
     protected abstract void CreateStateValue();
     protected void Initialize()
     {
         if (_stateMachineAsset == null)
         {
             Debug.LogError("资源为空");
         }
         if(_stateMachineAsset.InitialState==null)
         {
             Debug.LogError("初始状态为空");
         }
         else
         {
             _currentState = _stateMachineAsset.InitialState;
             _currentState.Enter(_currentStateValue);
         }
     }
     public BaseStateValue GetStateValue()=> _currentStateValue;
 }
 //状态机配置
 public class BaseStateMachineAsset : ScriptableObject 
 {
     public List<BaseTranslation> Translations;
     public BaseState InitialState;
     private Dictionary<StateType, List<BaseTranslation>> _map;
     public void OnValidate()
     {
         if(Translations != null&&Translations.Count>0)
         {
             BuildMap();
         }
     }
     public IReadOnlyList<BaseTranslation> GetTranslations(StateType type)
     {
         if(_map == null)
         {
             return null;
         }
         if(_map.TryGetValue(type,out var res))
         {
             return res.AsReadOnly();
         }
         return null;
     }
     private void BuildMap()
     {
         _map = new Dictionary<StateType, List<BaseTranslation>>();

         foreach (var translation in Translations)
         {
             if(!_map.ContainsKey(translation.From.Type))
             {
                 _map[translation.From.Type] = new List<BaseTranslation>();
             }
             _map[translation.From.Type].Add(translation);
         }
         foreach (var translations in _map.Values)
         {
             translations.Sort((a,b)=>b.Weight.CompareTo(a.Weight));
         }
     }
 }
 //上下文(数据容器)
public class BaseContext
{
    public GameObject Self;
    public bool IsHurt;
    public bool IsDead;
    public BaseContext(GameObject gameObject)
    {
        Self = gameObject;
        IsHurt = false;
        IsDead = false;
    }
}
//转换
 public abstract  class BaseTranslation : ScriptableObject 
 {
     public BaseState From;
     public BaseState To;
     public BaseCondition Condition;
     public int Weight;      //权重
 }

行为树

行为树采用树形结构组织行为,节点按功能分为三类:

组合节点控制子节点执行方式。Sequence顺序执行,子节点失败则中断;Selector选择执行,子节点成功则中断。这种隐式优先级避免了状态机中显式转换条件的膨胀。

修饰节点包装单个子节点,修改其返回值或执行逻辑。如Invert反转结果,Repeat循环执行,Cooldown限制频率。修饰器可嵌套,实现复杂控制流而不新增节点类型。

叶节点执行具体逻辑,包括条件判断(Condition)与动作执行(Action)。条件节点不改变世界状态,仅返回评估结果;动作节点可能持续多帧,返回Running表示未完成。

行为树每帧从根节点开始深度遍历,根据节点返回值决定遍历路径。这种"重新评估"机制天然支持反应式行为:高优先级条件(如受击)插入树前端,立即打断当前行为。

由于行为树的优势是可视化,而可视化需要用到GraphView编辑窗口,这里只贴出基础实现,项目中使用需要借助插件或者升级到Unity6

复制代码
 //返回节点状态
 public enum NodeState
 {
     Success,
     Failure,
     Running
 }
 //基础节点
 public abstract class BTNode
{
    public string Name;
    public abstract NodeState Execute();
}
//叶子节点
public abstract class ActionNode : BTNode
{
    protected Func<NodeState> _action;
    protected ActionNode(string name,Func<NodeState> act)
    {
        Name = name;
        _action = act;
    }
    public override NodeState Execute()
    {
        return _action();
    }
    
    public abstract class CompositeNode : BTNode
{
    protected List<BTNode> _children=new List<BTNode>();
    protected int currentIndex = 0;
    public void AddChild(BTNode child)
    {
        _children.Add(child);
    }
}
}
public class Selector : CompositeNode
{
    public override NodeState Execute()
    {
        for (int i = 0; i < _children.Count; i++)
        {
            var state = _children[i].Execute();
            if(state==NodeState.Success
                ||state==NodeState.Running)
            {
                return state;
            }
        }
        return NodeState.Failure;
    }
}
 public class Sequence : CompositeNode
 {
     public override NodeState Execute()
     {
         for (int i = 0; i < _children.Count; i++)
         {
             var state = _children[i].Execute();
             if(state==NodeState.Failure||
                 state==NodeState.Running)
             {
                 return state;
             }
         }
         return NodeState.Success;
     }
 }
 // 修饰器基类
public abstract class DecoratorNode : BTNode
{
    protected BTNode _child;
    
    public void SetChild(BTNode child) => _child = child;
}

// 反转结果
public class Inverter : DecoratorNode
{
    public override NodeState Execute()
    {
        var state = _child.Execute();
        return state switch
        {
            NodeState.Success => NodeState.Failure,
            NodeState.Failure => NodeState.Success,
            _ => NodeState.Running
        };
    }
}

// 重复执行N次
public class Repeater : DecoratorNode
{
    private int _repeatCount;
    private int _currentCount;
    
    public Repeater(int count)
    {
        _repeatCount = count;
        _currentCount = 0;
    }
    
    public override NodeState Execute()
    {
        while (_currentCount < _repeatCount)
        {
            var state = _child.Execute();
            if (state == NodeState.Running) return NodeState.Running;
            _currentCount++;
        }
        _currentCount = 0;
        return NodeState.Success;
    }
}

// 冷却时间
public class Cooldown : DecoratorNode
{
    private float _duration;
    private float _lastExecuteTime = float.MinValue;
    
    public Cooldown(float duration) => _duration = duration;
    
    public override NodeState Execute()
    {
        if (Time.time - _lastExecuteTime < _duration)
            return NodeState.Failure;
        
        var state = _child.Execute();
        if (state != NodeState.Running)
            _lastExecuteTime = Time.time;
        
        return state;
    }
}

// 行为树运行器
public class BehaviorTreeRunner : MonoBehaviour
{
    [SerializeField] private BTNode _root;
    
    void Update() => _root?.Execute();
    
    public void SetRoot(BTNode root) => _root = root;
}

结语

有什么不明白的欢迎在评论区讨论,年关将近,本文有些仓促

相关推荐
爱搞虚幻的阿恺8 天前
Niagara粒子系统-超炫酷的闪电特效(加餐 纸牌螺旋上升效果)
游戏·游戏引擎
_Li.8 天前
Simulink - 6DOF (Euler Angles)
人工智能·算法·机器学习·游戏引擎·cocos2d
weixin_424294678 天前
Unity 调用Steamworks API 的 SteamUserStats.RequestCurrentStats()报错
unity·游戏引擎·steamwork
HoFunGames8 天前
Unity小地图,Easy Minimap System MT-GPS插件
unity·游戏引擎
wy3258643648 天前
Unity 新输入系统InputSystem(基本操作)
unity·c#·游戏引擎
WarPigs8 天前
着色器multi_compile笔记
unity·着色器
ECHO飞跃 0128 天前
Unity2019 本地推理 通义千问0.5-1.5B微调导入
人工智能·深度学习·unity·llama
Unity游戏资源学习屋8 天前
【Unity UI资源包】GUI Pro - Casual Game 专为休闲手游打造的专业级UI资源包
ui·unity
冰凌糕8 天前
Unity3D Shader 顶点法线外扩实现描边效果
unity
星和月8 天前
Untiy使用说明
c#·游戏引擎