Unity——FSM有限状态机

2023-04-20,,

FSM有限状态机

一、设计思路

1.共同的状态父类,提供可重写的进入,保持,退出该状态的生命周期方法;

2.状态机,管理所有状态(增删查改),状态机运行方法(Run);

3.在角色控制器中,实例化状态机,并初始化添加状态;

二、关键类

1.StateBase

给物体所有状态提供的基类,所有状态比较继承这个基类,并且重写生命周期的方法;

泛型T为角色控制类;

字段:

public int stateID;		//状态ID,string也可
public T owner; //角色的实例

生命周期:

public abstract void OnEnter(params object[] args);	//进入状态调用
public abstract void OnStay(params object[] args); //保持状态调用
public abstract void OnExit(params object[] args); //退出状态调用

完整代码:

public abstract class StateBase<T>
{
//给每个状态设置一个ID
public int stateID;
public T owner; //拥有者(范型) public StateBase(int id,T o)
{
this.stateID = id;
owner = o;
} //给子类提供方法
public abstract void OnEnter(params object[] args);
public abstract void OnStay(params object[] args);
public abstract void OnExit(params object[] args);
}

2.StateMachine

private Dictionary<int, StateBase<T>> FSMActDic;	//存所有状态的字典
private int curState; //当前状态
public int nextState = 0; //下一个状态

关键方法:

切换状态时,调用当前方法的退出OnExit()后,继续调用下一个方法的进入OnEnter();

其他时候调用当前方法的OnStay();

public void FSMRun()
{
FSMActDic[curState].OnStay();
if (nextState != 0 && nextState != curState)
{
FSMActDic[curState].OnExit();
curState = nextState;
FSMActDic[curState].OnEnter();
nextState = 0;
}
}

完整代码:

public class FSM<T>
{
private Dictionary<int, StateBase<T>> FSMActDic;
private int curState;
public int nextState = 0; private void SwitchState()
{
if (nextState != 0 && nextState != curState)
{
FSMActDic[curState].OnExit();
curState = nextState;
FSMActDic[curState].OnEnter();
nextState = 0;
}
} public FSM(int id)
{
curState = id;
FSMActDic = new Dictionary<int, StateBase<T>>();
} //增
public void AddState(int id, StateBase<T> state)
{
if(FSMActDic.ContainsKey(id))
return; FSMActDic.Add(id, state);
} //删
public void RemoveSatate(int id)
{
if (FSMActDic.ContainsKey(id))
FSMActDic.Remove(id);
} //获取
public StateBase<T> GetState(int id)
{
if (!FSMActDic.ContainsKey(id))
return null; return FSMActDic[id];
} public void FSMRun()
{
FSMActDic[curState].OnStay();
SwitchState();
}
}

三、测试类

1.PlayerControl

实例状态机,添加几个状态,测试效果;这里我只做了简单的移动;

完整代码:

public class PlayerControl : MonoBehaviour
{
public enum PlayerState
{
none = 0,
idle,
move,
jump,
} private FSM<PlayerControl> mPlayerFSM;
public PlayerState mState;
public Animator mAnimator;
public float mSpeed;
public Vector3 moveDir; private void InitFSM()
{
mPlayerFSM.AddState((int) PlayerState.idle, new ActIdle((int) PlayerState.idle, this));
mPlayerFSM.AddState((int) PlayerState.move, new ActMove((int) PlayerState.move, this));
mPlayerFSM.AddState((int) PlayerState.jump, new ActScream((int) PlayerState.jump, this));
} void Start()
{
mAnimator = GetComponentInChildren<Animator>();
mSpeed = 10;
mPlayerFSM = new FSM<PlayerControl>((int) PlayerState.idle);
InitFSM();
mState = PlayerState.idle;
} void Update()
{
mPlayerFSM.FSMRun(); SwitchState(); mPlayerFSM.nextState = (int)mState;
} private void SwitchState()
{
if (Input.GetAxis("Horizontal") > 0 && Input.GetAxis("Vertical") == 0)
{
moveDir = transform.right;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") < 0 && Input.GetAxis("Vertical") == 0)
{
moveDir = -transform.right;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") > 0 && Input.GetAxis("Vertical") < 0)
{
moveDir = transform.right - transform.forward;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") < 0 && Input.GetAxis("Vertical") < 0)
{
moveDir = -transform.right - transform.forward;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") > 0 && Input.GetAxis("Vertical") > 0)
{
moveDir = transform.right + transform.forward;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") < 0 && Input.GetAxis("Vertical") > 0)
{
moveDir = -transform.right + transform.forward;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") == 0 && Input.GetAxis("Vertical") < 0)
{
moveDir = -transform.forward;
mState = PlayerState.move;
}
else if (Input.GetAxis("Horizontal") == 0 && Input.GetAxis("Vertical") > 0)
{
moveDir = transform.forward;
mState = PlayerState.move;
} if (Mathf.Abs(Input.GetAxis("Horizontal")) < 0.1f && Mathf.Abs(Input.GetAxis("Vertical")) < 0.1f)
mState = PlayerControl.PlayerState.idle; if (Input.GetAxis("Jump") != 0)
{
mState = PlayerState.jump;
}
}
}

2.行为类

设置的移动,待机,吼叫三个行为类;

代码展示:

public class ActMove : StateBase<PlayerControl>
{
public ActMove(int id, PlayerControl t) : base(id, t)
{
} //给子类提供方法
public override void OnEnter(params object[] args)
{
} public override void OnStay(params object[] args)
{
owner.mAnimator.Play("Walk");
owner.transform.position += owner.moveDir * Time.deltaTime * owner.mSpeed;
} public override void OnExit(params object[] args)
{
}
}

四、Demo展示

以上是我对FSM的总结,如果有更好的意见,欢迎给作者评论留言;

Unity——FSM有限状态机的相关教程结束。

《Unity——FSM有限状态机.doc》

下载本文的Word格式文档,以方便收藏与打印。