使用Unity Animator 动画状态机实现有限状态机
- 创建Animator 并编辑
- 一般创建空状态,也可以创建带有动画的状态
- 把动画切换过度关掉
- 使用
Animator Parameters
控制状态切换

- 创建一个
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("完成出门");
});
}
}

/*
* 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;
}
}

注意事项
- FSM_Animator 内的
Update
并不是与Unity Update
同步 ,我用的是异步无限循环。你也可以通过注释掉异步,在MonoBehaviour Update
调用执行。
- Animator 默认在动画状态机第一层级执行状态
- 添加状态任务要对应
Animator state
节点 名称

- 通过改变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;
}
}
}