FSM简介
FSM定义:
一个有限状态机是一个设备,或者是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得一个状态变换到另一个状态,或者是使一个输入或者一种行为的发生。一个有限状态机在任何瞬间只能处在一种状态。
它的优点:
1.编程快速简单,2.易于调试,3.很少的计算开销,4.直觉性,5.灵活性。
简单的框架
主要的两个类,FSMState表示状态,FSMSystem里面维护了一个状态的列表,最后需要一个StateController作为状态的控制器。
FSMState.cs
using UnityEngine;
using System.Collections.Generic;
public enum Transition
{
NullTransition = 0, // Use this transition to represent a non-existing transition in your system
SawPlayer,
LostPlayer,
NoHealth,
ReadytoAim,
ReadytoShot,
ReadytoIdle,
ReadytoAttack,
ReadytoChasing
}
public enum StateID
{
NullStateID = 0, // Use this ID to represent a non-existing State in your system
Idle,
Chasing, // jump
Attack,
Shooting,
Aiming,
BacktoIdle,//jump
Dead,
}
public abstract class FSMState
{
protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();// 过度条件<=>状态ID 映射表
protected StateID stateID; // 状态ID
public StateID ID { get { return stateID; } } // 只读状态ID
public void AddTransition(Transition trans, StateID id) // 添加映射
{
// Check if anyone of the args is invalid
if (trans == Transition.NullTransition) //返回
{
Debug.LogError("FSMState ERROR: NullTransition is not allowed for a real transition");
return;
}
if (id == StateID.NullStateID) //返回
{
Debug.LogError("FSMState ERROR: NullStateID is not allowed for a real ID");
return;
}
// Since this is a Deterministic FSM,
// check if the current transition was already inside the map
if (map.ContainsKey(trans))
{
Debug.LogError("FSMState ERROR: State " + stateID.ToString() + " already has transition " + trans.ToString() +
"Impossible to assign to another state");
return;
}
map.Add(trans, id);
}
/// <summary>
/// This method deletes a pair transition-state from this state's map.
/// If the transition was not inside the state's map, an ERROR message is printed.
/// </summary>
public void DeleteTransition(Transition trans) // 删除映射
{
// Check for NullTransition
if (trans == Transition.NullTransition)
{
Debug.LogError("FSMState ERROR: NullTransition is not allowed");
return;
}
// Check if the pair is inside the map before deleting
if (map.ContainsKey(trans))
{
map.Remove(trans);
return;
}
Debug.LogError("FSMState ERROR: Transition " + trans.ToString() + " passed to " + stateID.ToString() +
" was not on the state's transition list");
}
/// <summary>
/// This method returns the new state the FSM should be if
/// this state receives a transition and
/// </summary>
public StateID GetOutputState(Transition trans) // 获取输出状态ID
{
// Check if the map has this transition
if (map.ContainsKey(trans))
{
return map[trans];
}
return StateID.NullStateID;
}
/// <summary>
/// This method is used to set up the State condition before entering it.
/// It is called automatically by the FSMSystem class before assigning it
/// to the current state.
/// </summary>
public virtual void DoBeforeEntering() { } // 进入状态
/// <summary>
/// This method is used to make anything necessary, as reseting variables
/// before the FSMSystem changes to another one. It is called automatically
/// by the FSMSystem before changing to a new state.
/// </summary>
public virtual void DoBeforeLeaving() { } // 离开状态
/// <summary>
/// This method decides if the state should transition to another on its list
/// NPC is a reference to the object that is controlled by this class
/// </summary>
public abstract void Reason(FSMSystem fsms); // 检测
/// <summary>
/// This method controls the behavior of the NPC in the game World.
/// Every action, movement or communication the NPC does should be placed here
/// NPC is a reference to the object that is controlled by this class
/// </summary>
public abstract void Act(GameObject player = null, GameObject npc = null); // 执行
}
FSMSystem.cs
using UnityEngine;
using System.Collections.Generic;
public class FSMSystem : MonoBehaviour
{
private List<FSMState> states; //状态集合
// The only way one can change the state of the FSM is by performing a transition
// Don't change the CurrentState directly
private StateID currentStateID; //当前状态ID
public StateID CurrentStateID { get { return currentStateID; } }
private FSMState currentState; //当前状态
public FSMState CurrentState { get { return currentState; } }
public StateID defaultState { set { defaultState = value; } get { return defaultState; } } //默认状态ID
public void resetToDefaultState() //重置默认
{
//currentState = states[0];
//currentStateID = states[0].ID;
/*for(int i =0; i< states.Count; i++)
{
if(states[i].ID == defaultState)
{
currentState = states[i];
currentStateID = states[i].ID;
}
}*/
}
public FSMSystem()
{
states = new List<FSMState>();
}
/// <summary>
/// This method places new states inside the FSM,
/// or prints an ERROR message if the state was already inside the List.
/// First state added is also the initial state.
/// </summary>
public void AddState(FSMState s) //增加状态
{
// Check for Null reference before deleting
if (s == null)
{
Debug.LogError("FSM ERROR: Null reference is not allowed");
}
// First State inserted is also the Initial state,
// the state the machine is in when the simulation begins
if (states.Count == 0)
{
states.Add(s);
currentState = s;
currentStateID = s.ID;
return;
}
// Add the state to the List if it's not inside it
foreach (FSMState state in states)
{
if (state.ID == s.ID)
{
Debug.LogError("FSM ERROR: Impossible to add state " + s.ID.ToString() +
" because state has already been added");
return;
}
}
states.Add(s);
}
/// <summary>
/// This method delete a state from the FSM List if it exists,
/// or prints an ERROR message if the state was not on the List.
/// </summary>
public void DeleteState(StateID id) //删除状态
{
// Check for NullState before deleting
if (id == StateID.NullStateID)
{
Debug.LogError("FSM ERROR: NullStateID is not allowed for a real state");
return;
}
// Search the List and delete the state if it's inside it
foreach (FSMState state in states)
{
if (state.ID == id)
{
states.Remove(state);
return;
}
}
Debug.LogError("FSM ERROR: Impossible to delete state " + id.ToString() +
". It was not on the list of states");
}
/// <summary>
/// This method tries to change the state the FSM is in based on
/// the current state and the transition passed. If current state
/// doesn't have a target state for the transition passed,
/// an ERROR message is printed.
/// </summary>
public void PerformTransition(Transition trans) //执行过度状态
{
// Check for NullTransition before changing the current state
if (trans == Transition.NullTransition)
{
Debug.LogError("FSM ERROR: NullTransition is not allowed for a real transition");
return;
}
// Check if the currentState has the transition passed as argument
StateID id = currentState.GetOutputState(trans);
if (id == StateID.NullStateID)
{
Debug.LogError("FSM ERROR: State " + currentStateID.ToString() + " does not have a target state " +
" for transition " + trans.ToString());
return;
}
// Update the currentStateID and currentState
currentStateID = id;
foreach (FSMState state in states)
{
if (state.ID == currentStateID)
{
// Do the post processing of the state before setting the new one
currentState.DoBeforeLeaving();
currentState = state;
// Reset the state to its desired condition before it can reason or act
currentState.DoBeforeEntering();
break;
}
}
} // PerformTransition()
//
void Awake()
{
FSMState Chasing = new ChasingState();
Chasing.AddTransition(Transition.LostPlayer, StateID.Idle);
AddState(Chasing);
FSMState Idle = new IdleState();
Idle.AddTransition(Transition.SawPlayer, StateID.Chasing);
AddState(Idle);
}
//
void Update()
{
if (currentState!=null)
{
currentState.Act();
currentState.Reason(this);
}
}
public bool Chasing = false;
public bool Idle = false;
void OnGUI()
{
if (GUI.Button(new Rect(50,50,100,50),"Chasing"))
{
Chasing = true;
Idle = false;
}
if (GUI.Button(new Rect(50, 150, 100, 50), "Idle"))
{
Chasing = false;
Idle = true;
}
}
}
状态实例
IdleState.cs
using UnityEngine;
public class IdleState : FSMState
{
public IdleState()
{
stateID = StateID.Idle;
}
float time = 0;
float timeD = 1;
public override void Act(GameObject player = null, GameObject npc = null)
{
time += Time.deltaTime;
if (time > timeD)
{
time = 0;
Debug.Log("@@ IdleState Act");
}
}
public override void Reason(FSMSystem fsms)
{
//判断条件
if (fsms.Chasing)
{
fsms.PerformTransition(Transition.SawPlayer);
Debug.Log("@@ IdleState Reason");
}
}
public override void DoBeforeLeaving()
{
Debug.Log("@@ IdleState Leaving");
}
public override void DoBeforeEntering()
{
Debug.Log("@@ IdleState Entering");
}
}
ChasingState.cs
using UnityEngine;
public class ChasingState : FSMState
{
public ChasingState()
{
stateID = StateID.Chasing;
}
float time = 0;
float timeD = 1;
public override void Act(GameObject player = null, GameObject npc = null)
{
time += Time.deltaTime;
if (time > timeD)
{
time = 0;
Debug.Log("@@ ChasingState Act");
}
}
public override void Reason(FSMSystem fsms)
{
//判断条件
if (fsms.Idle)
{
fsms.PerformTransition(Transition.LostPlayer);
Debug.Log("@@ ChasingState Reason");
}
}
public override void DoBeforeLeaving()
{
Debug.Log("@@ ChasingState Leaving");
}
public override void DoBeforeEntering()
{
Debug.Log("@@ ChasingState Entering");
}
}