Unity中状态机的使用

来源:互联网 发布:web安全工程师 知乎 编辑:程序博客网 时间:2024/06/08 09:11

Unity中状态机的使用

在游戏中,人物的状态是不断变化的,所以写个FSM来管理状态是必要的。
一个有限状态机是一个设备,或者是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得一个状态变换到另一个状态,或者是使一个输入或者一种行为的发生。一个有限状态机在任何瞬间只能处在一种状态。

设置状态的常量

我们这里设置了几个普通的idle,run,death的常量

public class ConstStrings{    //状态 常量    public const string St_Born = "born";//出生    public const string St_Idle = "idle";//静止    public const string St_FunnyIdle = "funnyidle";//休闲动作    public const string St_Run = "run";//移动    public const string St_Dead = "death";//死亡    public const string St_Relive = "relive";//复活    public const string St_Jump = "jump";//跳跃}

定义状态图

/// <summary>/// 状态图,存储角色可以切换的所有状态/// </summary>public class CharStateGraph{    /// <summary>    /// 状态图的名称    /// </summary>    public string StateGraphName;    /// <summary>    /// 存储角色所有的状态    /// </summary>    public Dictionary<string, CharSateBase> states = new Dictionary<string, CharSateBase>();    /// <summary>    /// 仅用作当前状态的定时器    /// 切换状态时清空    /// </summary>    public uint[] framtimers;    public uint[] timers;    public CharStateGraph()    {        framtimers = new uint[10];        timers = new uint[10];    }    ~CharStateGraph()    {        StopTimers();        framtimers = null;        timers = null;    }

下面来定义一般的状态图,继承于上面的状态图。

/// <summary>/// 一般状态图/// </summary>public class SGCommon : CharStateGraph{    public SGCommon()    {        CommonStates.AddBorn(states);        CommonStates.AddIdle(states);        CommonStates.AddFunnyIdle(states);        CommonStates.AddAttack(states);        CommonStates.AddMoveAttack(states);        CommonStates.AddRun(states);        CommonStates.AddDeath(states);    }}

状态的切换

在状态图里面,常需要的就是切换状态的行为,改变人物的状态。
这个了类是用来管理状态机的,就是在实际应用时候去调用的了。

/// <summary>/// 状态图实例,用于实现状态切换等功能/// </summary>public class StateGraphInstance{    /// <summary>    /// 状态图    /// </summary>    public CharStateGraph sg;    /// <summary>    /// 当前状态    /// </summary>    public CharSateBase currentState;    /// <summary>    /// 前一个状态    /// </summary>    public CharSateBase preState;    /// <summary>    /// 角色实体    /// </summary>    public BaseController inst;    public Delegate OnStop;    public Delegate OnStart;    public StateGraphInstance(CharStateGraph sg, BaseController inst)    {        this.sg = sg;        this.inst = inst;    }    ~StateGraphInstance()    {        sg = null;        currentState = null;        preState = null;        OnStop = null;        OnStart = null;        inst = null;    }    public void Start()    {        if (OnStart != null)            OnStart.DynamicInvoke();    }    public void Update()    {        if (currentState != null)        {            currentState.Update(inst, Time.time);        }    }    public void Stop()    {        sg.StopTimers();        sg = null;        if (OnStop != null)            OnStop.DynamicInvoke();        currentState = null;        preState = null;        inst = null;        OnStop = null;        OnStart = null;    }    /// <summary>    /// 进入状态    /// </summary>    /// <param name="statename"></param>    /// <param name="objs"></param>    public void GoToState(string statename, params object[] objs)    {        CharSateBase state;        if (sg.states.TryGetValue(statename, out state))        {            preState = currentState;            if (preState != null)                preState.ClearAddTags();            if (currentState != null && currentState.onexit != null)            {                currentState.Exit(inst, objs);            }            sg.StopTimers();            currentState = state;            currentState.Enter(inst, objs);        }    }

角色的状态

另外还定义了角色的状态,角色状态的名字,角色的状态的进入和退出。
其实这里的 CharStateGraph就是状态机,里面保存状态图的简单信息,名字,停止时间等。
CharStateBase就是普通的状态。
在状态图里面,我们使用来管理状态图的。相当于这个就是一个状态机,状态机和状态的关系是一对多的关系。

在CharStateBase里面有三个常用的状态的改变,状态进入Enter,更新Update,退出Exit。
这里写图片描述

/// <summary>/// 角色状态/// </summary>public class CharSateBase{    /// <summary>    /// 状态名字    /// </summary>    public string statename;    /// <summary>    /// 状态开始的时间    /// </summary>    private float starttime;    /// <summary>    /// 状态的标签    /// </summary>     // busy   表示上层是否可以操控  // atomic 表示状态之间是否不被打断 // canrotate // norotate // canmove // nomove    public HashSet<string> tags;    /// <summary>    /// 状态额外加的标签    /// </summary>    public HashSet<string> addTags;    /// <summary>    /// 状态进入是的委托    /// </summary>    public Delegate onenter;    /// <summary>    /// 状态退出的委托    /// </summary>    public Delegate onexit;    /// <summary>    /// 状态更新的事件    /// </summary>    public Action<BaseController, float> onupdate;    /// <summary>    /// 状态中监听的事件    /// </summary>    /// <summary>    /// 进入状态    /// </summary>    public void Enter(BaseController bc, params object[] objs)    {        starttime = Time.time;        if (onenter != null)        {            onenter.DynamicInvoke(bc, objs);        }    }    public void Update(BaseController bc,float curtime)    {        if (onupdate != null)        {            onupdate.DynamicInvoke(bc, curtime - starttime);        }    }    /// <summary>    /// 退出状态    /// </summary>    public void Exit(BaseController bc,params object[] objs)    {        if (onexit != null)        {            onexit.DynamicInvoke(bc,objs);        }    }

其实上面的是状态机的名字的改变,实质上还是普通的状态机,下面看下常见的状态机模式。

常见的状态机模式

Istate

Istate是状态机的抽象基类。以后的很多的状态就可以去继承这个基类的。

public abstract class IState<T,F>{    public T Owner { get; set; }    public F Fsm { get; set; }    public IStateParam baseparam { get; private set; }    public void SetParam(IStateParam _param)    {        baseparam = _param;    }    public void SetFSMID(F id)    {        Fsm = id;    }    public abstract bool CheckTransition();    public abstract void Enter();    public abstract void Execute();    public abstract void Exit();}

我们现在使用的是来管理摄像机的一些状态,下面只去列举一种摄像机的状态,实际上有很多种的。如若不是,实现状态机的意义也就是没有的了。

// 相机展示时装直接跟随public class CamFashionShowFollowState : IState<FreeLookCam,FSMState>{    public CamFashionShowFollowParam mDirparam;    public override bool CheckTransition()    {        return true;    }    public override void Enter()    {#if UNITY_EDITOR        Debug.Log("CamFashionShowFollowState  " + Time.time);#endif        mDirparam = baseparam as CamFashionShowFollowParam;        FreeLookCam.Instance.CanZoom = false;        FreeLookCam.Instance.CanRotate = true;    }    public override void Execute()    {        if (FreeLookCam.Instance != null)            FreeLookCam.Instance.DirFollowTarget();    }    public override void Exit()    {    }}

状态机IstateMachine类

public class IStateMachine<T, F>{    private T Owner { get; set; }    private Dictionary<F, IState<T, F>> mStates;    private IState<T, F> mCurrState;    private IState<T, F> mPrevState;    private IState<T, F> mGlobalState;    public IStateMachine(T owner)    {        this.Owner = owner;        mStates = new Dictionary<F, IState<T, F>>();    }    public bool Contains(F fsmID)    {        return this.mStates.ContainsKey(fsmID);    }    public void AddState(F fsmID, IState<T, F> state)    {        if (Contains(fsmID))        {            return;        }        state.SetFSMID(fsmID);        state.Owner = Owner;        mStates.Add(fsmID, state);    }    public IState<T, F> GetState(F fsmID)    {        if (Contains(fsmID))        {            return mStates[fsmID];        }        return null;    }    public void ChangeState(IState<T, F> newState)    {        mPrevState = mCurrState;        mCurrState.Exit();        mCurrState = newState;        mCurrState.Enter();    }    public void ChangeState(F newFSM)    {        IState<T, F> newState = GetState(newFSM);        if (newState != null)        {            ChangeState(newState);        }    }    public void SetCurrState(IState<T, F> fsm)    {        mCurrState = fsm;    }    public IState<T, F> GetCurState()    {        return mCurrState;    }    public F GetCurrStateID()    {        if (mCurrState == null)        {            return default(F);        }        return mCurrState.Fsm;    }    public void Start()    {        if (mCurrState != null)        {            mCurrState.Enter();        }        if (mGlobalState != null)        {            mGlobalState.Enter();        }    }}

这里就是差不多就要完成的了,上文还有一个实例化状态机的类StateGraphInstance,这是用来管理状态机的,在这里就不如列举了。