前言
参考资料:unity3D FSM有限状态机(状态设计模式)
核心
先列举有限数量的状态,让需要被控制的物体在状态中根据设定流转,并且每次只存在一个状态被激活。
三个方案
- 声明一个enum字典,写入所有的状态
public enum EnemyStateEnum
{
IDLE,//待机 -> 追击/死亡
TRACE,//追击 -> 攻击/丢失/死亡
LOST,//丢失 -> 待机/追击/死亡
ATK1,//近战 -> 追击/丢失/死亡
DIE,//死亡
}
后面是我想要让他能够流转的关系(提前写一下)
方案一 switch
public EnemyStateEnum traceState = EnemyStateEnum.IDLE;//声明一个状态属性
void Update()
{
switch (traceState)
{
case EnemyStateEnum.IDLE:
要做啥的事情();
break;
case EnemyStateEnum.TRACE:
要做啥的事情();
break;
case EnemyStateEnum.LOST:
要做啥的事情();
break;
case EnemyStateEnum.ATK1:
要做啥的事情();
break;
case EnemyStateEnum.DIE:
要做啥的事情();
break;
}
}
然后在应触发转换的位置进行触发就行了。
这是非常偷懒的办法,并且局限性很大,如果状态非常多,或者想要区分状态开始、中间、结束,会非常麻烦
我写了很久才发现上面的有问题……紧急学了以下内容
方案二 参考资料:unity3D FSM有限状态机(状态设计模式)
1. 状态基类
状态基类需要3个虚方法,以及虚拟机对象
2. 拥有者泛型类
继承状态基类,构造出使用者对象
3. 玩家脚本类
在Start方法里构造new各个状态机类(传入枚举id,使用者this(自己)),new状态机对象,添加各个状态类对象。
在Update通过按键,并一直调用状态机的切换方法。
在LateUpdate()调用状态机的保持状态方法
4. 状态机类
包含添加状态类,切换状态类方法(控制Enter()),保存状态类方法(控制(Stay()))
加若干个状态类
每个状态具体有3种状态方法(OnEnter,OnStay,OnExit)需要重写来具体实现,通过OnExit()方法改变使用者的枚举id来切换状态
我做了一些调整,让onEnter每次只需单次调用
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// 状态机器类:由Player控制。完成状态的存储,切换,和状态的保持
/// </summary>
public class StateMachine
{
//用来存储当前机器所控制的所有状态
public Dictionary<int, StateBase> m_StateCache;
//定义当前状态
public StateBase m_currentState;
//机器初始化时,没有上一个状态
public StateMachine(StateBase beginState)
{
m_currentState = beginState;
m_StateCache = new Dictionary<int, StateBase>();
//把状态添加到集合中
AddState(beginState);
m_currentState.OnEnter();
}
public void AddState(StateBase state)
{
if (!m_StateCache.ContainsKey(state.ID))
{
m_StateCache.Add(state.ID, state);
state.machine = this;
}
}
//通过Id来切换状态
public void TranslateState(int id)
{
if (!m_StateCache.ContainsKey(id))
{
Debug.Log("未添加ID");
return;
}
if (m_currentState.ID == id)
{
Debug.Log("正处于"+ m_StateCache[id] + "状态");
return;
}
m_currentState.OnExit();
m_currentState = m_StateCache[id];
m_currentState.OnEnter();
Debug.Log("切换至" + m_StateCache[id] + "状态");
}
//状态保持
public void Update()
{
if (m_currentState != null)
{
m_currentState.OnStay();
}
}
}
其他注意的点
- 如果你改写了玩家脚本,记得要在
LateUpdate()
中进行调用,不然会拿不到stay()
状态。 - 可以将所有行为写在一个脚本文件的不同类里
- 并且通过owner关键字可以调用到你关联的脚本
方案三
利用unity的动画状态机
创建空的动画用来表示事件,并通过bool开关进行控制