Unity 简单状态机

使用Unity Animator 动画状态机实现有限状态机

  • 创建Animator 并编辑
  • 一般创建空状态,也可以创建带有动画的状态
  • 把动画切换过度关掉
  • 使用Animator Parameters 控制状态切换
    image
  • 创建一个text.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using System;
using UnityEngine.UI;

public class test : MonoBehaviour
{
    public Animator m_animator;
    private FSM_Animator fsm;
    private void Start()
    {
        FSM_Animator fsm = new FSM_Animator(m_animator);
        fsm["起床"] = new FSM_Action(() =>
          {
              Debug.Log("准备起床");
          },()=>
          {
              Debug.Log("真在起床");
          },()=>
          {
              Debug.Log("结束起床");
          },()=>
          {
              Debug.Log("完成起床");
          });
        fsm["出门"] = new FSM_Action(() =>
        {
            Debug.Log("准备出门");
        }, () =>
        {
            Debug.Log("真在出门");
        }, () =>
        {
            Debug.Log("结束出门");
        }, () =>
        {
            Debug.Log("完成出门");
        });
    }
}

image

  • 添加 编写好的插件
/*
 *    Demo:给予Unity Animator 实现状态机
 *    FSM_Animator fsm = new FSM_Animator(m_animator);
 *    fsm["起床"] = new FSM_Action(() =>
 *    {
 *    Debug.Log("准备起床");
 *    }, () =>
 *    {
 *    Debug.Log("真在起床");
 *    }, () =>
 *    {
 *    Debug.Log("结束起床");
 *    }, () =>
 *    {
 *    Debug.Log("完成起床");
 *    });
 *    fsm["出门"] = new FSM_Action(() =>
 *    {
 *    Debug.Log("准备出门");
 *    }, () =>
 *    {
 *    Debug.Log("真在出门");
 *    }, () =>
 *    {
 *    Debug.Log("结束出门");
 *    }, () =>
 *    {
 *    Debug.Log("完成出门");
*/


using System;
using UnityEngine;
using System.Threading.Tasks;
using System.Collections.Generic;

public class FSM_Animator
{
    private Animator m_animator;
    private Dictionary<int, FSM_Action> m_fsm;// 状态事件集合
    private float m_playbackProgress = 0.9f;    // 动画播放进度
    private int m_currentState;             // 当前状态
    private int m_currentId                 // 当前状态id
    {
        get
        {
            return m_currentState;
        }
        set
        {
            if (m_currentState != value)
            {
                this[m_currentState]?.Exit?.Invoke();
                this[value]?.Enter?.Invoke();
                m_currentState = value;
            }
        }
    }
    public FSM_Animator(Animator animator)// 构造函数
    {
        m_animator = animator;
        Start();
    }
    public FSM_Action this[string state]
    {
        get
        {
            int id = StringToHash(state);
            return this[id];
        }
        set
        {
            int id = StringToHash(state);
            if (value == null)// 移除
            {
                m_fsm.Remove(id);   // 有则返回 TRUE ,没有则返回 FALSE
            }
            else
            {
                m_fsm[id] = value; // 有则修改,没有则添加
            }
        }
    }
    public FSM_Action this[int state]
    {
        get
        {
            FSM_Action _Action;
            m_fsm.TryGetValue(state, out _Action);
            return _Action;
        }
    }
    private void Start()
    {
        m_fsm = new Dictionary<int, FSM_Action>();
        Update();
    }
    /// <summary>
    /// 如果要同步Update可将其设置为共有并在MonoBehaviour Update调用
    /// </summary>
    private async void Update()
    {
        int time = 20;  // 一帧时间
        while (Application.isPlaying)
        {
            await Task.Delay(time);
            AnimatorStateInfo info = m_animator.GetCurrentAnimatorStateInfo(0);

            this[m_currentId]?.Update?.Invoke();
            //if (info.normalizedTime >= m_playbackProgress)
            //{
            //    this[m_currentId]?.Finished?.Invoke();
            //}
            m_currentId = info.fullPathHash;
        }
    }
    private int StringToHash(String state)
    {
        // 层级+点+状态
        // 层级+点+子状态+点+状态
        // LayerName+"."+StateName[StateMachine+"."+StateName]
        state = m_animator.GetLayerName(0) + "." + state;
        return Animator.StringToHash(state);
    }
    private bool HasState(int stateID)
    {
        return m_animator.HasState(0, stateID);
    }
    private bool HasState(string stateName)
    {
        return m_animator.HasState(0, StringToHash(stateName));
    }
}
public class FSM_Action
{
    public Action Enter;// 进入-执行一次
    public Action Update;// 更新-执行多次
    public Action Exit;//退出-- 执行一次
    public FSM_Action(Action _enter, Action _update = null, Action _exit = null)
    {
        Enter = _enter;
        Update = _update;
        Exit = _exit;
    }
}
  • 点击运行

image

注意事项

  • FSM_Animator 内的Update并不是与Unity Update同步 ,我用的是异步无限循环。你也可以通过注释掉异步,在MonoBehaviour Update 调用执行。
  • Animator 默认在动画状态机第一层级执行状态
  • 添加状态任务要对应Animator state 节点 名称
    image
  • 通过改变Unity 控制动画字段来控制状态切换

模拟Unity Animator

  • 通过控制该类字段来控制状态切换
  • 先注册所有要切换的功能
  • 在需要切换状态的地方改变FSM_Manager.getInstance().CurrentState字段即可
  • 主要代码实现
/*
 *  Demo: 简单状态机
 *  FSM_Manager.getInstance().Add("跑步", () =>
 *   {
 *       Debug.Log("跑步");
 *   });
 *  FSM_Manager.getInstance().Add("睡觉", () =>
 *   {
 *       Debug.Log("睡觉");
 *   });
 *  FSM_Manager.getInstance().Add("上班", () =>
 *   {
 *       Debug.Log("上班");
 *   });
 *  FSM_Manager.getInstance().CurrentState = "";
 *  FSM_Manager.getInstance().CurrentState = "上班";  // 上班
 *  FSM_Manager.getInstance().CurrentState = "睡觉";  // 睡觉
 */
using System;
using System.Collections.Generic;

public class FSM_Manager
{
    private Dictionary<String, Action> m_fsm;
    private string m_currentState;
    #region 单例
    private FSM_Manager() { Init(); }
    private static FSM_Manager instance;
    public static FSM_Manager getInstance()
    {
        if (instance == null)
        {
            instance = new FSM_Manager();
        }
        return instance;
    }
    #endregion
    #region 私有
    private void Init() // TODO 初始化
    {
        m_currentState = "";
        m_fsm = new Dictionary<string, Action>();
    }
    private void StateChange()// TODO 状态改变调用
    {
        m_fsm[m_currentState]?.Invoke();
    }
    #endregion
    public string CurrentState
    {
        get
        {
            return m_currentState;
        }
        set
        {
            if (m_currentState != value)
            {
                m_currentState = value;
                StateChange();
            }
        }
    }
    public void Add(string state, Action action)
    {
        if (!m_fsm.ContainsKey(state))
        {
            m_fsm.Add(state, action);
        }
    }
    public void Remove(string state)
    {
        m_fsm.Remove(state);// 如果成功找到并移除该元素,则为 true;否则为 false
    }
    public Action this[string index]
    {
        get
        {
            return m_fsm[index];
        }
        set
        {
            m_fsm[index] = value;
        }
    }
}
posted @ 2022-09-27 18:55  镜子-眼泪  阅读(458)  评论(0)    收藏  举报