unity 有限状态机的简单框架 理解 运用

8 篇文章 2 订阅

有限状态机这种东西 或者说状态这种东西  真的是你运用到了,才能更好的理解。啥是状态,其实这个在开发者思维里,就是一种阶段,或者某个时刻,某个条件下的
      事物的集合,例如一个角色的走路状态,休息状态,跑步状态,或者一个UI菜单面板的保存,加载,退出,新建,你都可以把他们归为一个一个状态。甚至游戏的,登录,登录验证,登录成功,登录失败,初始化都可以为一个一个状态。看来状态机真的是用到的地方非常多的,并不一定是在角色身上的状态,很多时候,只要是不同的逻辑划分,我们就可以归为一个状态。

我举例的状态机是别人  商业级的代码剥离出来的,代码是转载的,文笔不好,还是直接上代码解释。

首先是状态机,状态机管理着,状态的进入,状态的退出,状态的执行,状态机里有一个严格的规定
那就是,当有一个新状态进入的时候,必定会有当前的状态的退出,这个很重要,状态的执行由状态机掌控,这个状态机设计应该是最简单的了,我们如果能够熟练运用后,在碰到实际问题时候,我们才能够在这个的基础上来修改,完善它。可以加上自己的见解,然后抽象出来,形成一个新的功能更加强大的状态机,要理解这个状态机,还得了解泛型编程,理解泛型编程才能够很好的理解这个状态机。 

基础状态机如下

using UnityEngine;
using System.Collections;

/// <summary>
/// 状态机
/// 使用这个来驱动各种状态
/// </summary>
/// <typeparam name="entity_type"></typeparam>
public class FsmStateMachine<entity_type>
{
    //状态持有对象
    private entity_type m_pOwner;

    //
    private FsmState<entity_type> m_pCurrentState;//当前状态
    private FsmState<entity_type> m_pPreviouState;//上一个状态
    private FsmState<entity_type> m_pGlobalState;//全局状态


    /// <summary>
    /// 状态机的构造函数
    /// </summary>
    /// <param name="owner"></param>
    public FsmStateMachine(entity_type owner)
    {
        m_pOwner = owner;
        m_pPreviouState = null;
        m_pCurrentState = null;
        m_pGlobalState = null;
    }

    /// <summary>
    /// 设置当前状态
    /// </summary>
    /// <param name="GlobalState"></param>
    public void SetCurrentState(FsmState<entity_type> CurrentState)
    {
        if (CurrentState != null)
        {
            //保存 当前状态
            m_pCurrentState = CurrentState;
            //设置 状态中的 Target 为
            m_pCurrentState.Target = m_pOwner;
            m_pCurrentState.Enter();
        }
    }


    /// <summary>
    /// 进入全局状态
    /// 进行一些数据的刷新,比如说距离的计算等
    /// </summary>
    /// <param name="GlobalState"></param>
    public void SetGlobalState(FsmState<entity_type> GlobalState)
    {
        if (GlobalState != null)
        {
            m_pGlobalState = GlobalState;
            m_pGlobalState.Target = m_pOwner;
            m_pGlobalState.Enter();
        }
        else
        {
            Debug.LogError("不能设置空状态");
        }
    }


    /// <summary>
    /// 进入全局状态d
    /// </summary>
    public void GlobalStateEnter()
    {

    }






    /// <summary>
    /// Update方法
    /// </summary>
    public void SmUpdate()
    {
        //只要设置了.就会一直执行
        if (m_pGlobalState != null)
        {
            m_pGlobalState.Excute();
        }
        if (m_pCurrentState != null)
        {
            m_pCurrentState.Excute();
        }
    }


    /// <summary>
    /// 切换状态
    /// </summary>
    /// <param name="pNewState">希望变成的状态</param>
    public void ChangeState(FsmState<entity_type> pNewState)
    {

        if (pNewState == null)
        {
            Debug.Log("不能使用空的状态");
        }
        //触发状态退出方法
        m_pCurrentState.Exit();

        //保存上一状态
        m_pPreviouState = m_pCurrentState;

        //设置新状态为当前状态
        m_pCurrentState = pNewState;
        //传递 状态 使用 对象
        m_pCurrentState.Target = m_pOwner;

        //触发当前状态调用Enter方法
        m_pCurrentState.Enter();
    }

    /// <summary>
    /// 切换回上一状态
    /// </summary>
    public void RevertToPreviousState()
    {
        this.ChangeState(m_pPreviouState);
    }

    /// <summary>
    /// 获取当前状态
    /// </summary>
    /// <returns></returns>
    public FsmState<entity_type> Get_CurrentState()
    {
        return m_pCurrentState;
    }

    public FsmState<entity_type> Get_GlobalState()
    {
        return m_pGlobalState;
    }



    /// <summary>
    /// 获取上一状态
    /// </summary>
    /// <returns></returns>
    public FsmState<entity_type> Get_PreviousState()
    {
        return m_pPreviouState;
    }
}

接下来我们实现状态基类

状态类很简单,就是进入,退出,执行,还有一个使用状态的对象


using UnityEngine;
/// <summary>
/// 状态基类,定义了状态的基础方法
/// 为什么需要定义为3个呢
/// 是因为有些逻辑都是进入状态或者退出状态的时候执行的,
/// 所以这里将逻辑分开:
/// 分为:
/// 1>进入状态,
/// 2>状态执行,
/// 3>状态退出
/// </summary>
/// <typeparam name="entity_type">使用泛型,哪个状态使用,就传入哪个状态</typeparam>
public class FsmState<entity_type>
{

    /// <summary>
    /// 使用状态的对象
    /// </summary>
    public entity_type Target;

    /// <summary>
    /// 进入状态的逻辑
    /// 只执行一次的逻辑
    /// </summary>
    /// <param name="entityType"></param>
    public virtual void Enter()
    {
        Debug.Log(this.ToString()+"    Enter");
    }

    /// <summary>
    /// 执行状态的逻辑
    /// </summary>
    /// <param name="entityType"></param>
    public virtual void Excute()
    {
    
    
    }

    /// <summary>
    /// 退出状态的逻辑
    /// </summary>
    /// <param name="entityType"></param>
    public virtual void Exit()
    {
        Debug.Log(this.ToString() + "    Exit");
    }


}

好了,状态机基本搭建好了,是不是很简单,其实简单就好,功能复杂的功能都是分解成一个一个小功能去实现的,何况是基层代码,越简单越好,越简单越稳,然后呢,我们要的就是这种基层代码的约束,比如,有状态退出,必定有状态的进入,我觉得这个就是状态机的核心功能,在这个约束规则上做我们的事。上层代码才能够很好的挥洒功能。

 

下面我们就新建一个简单的状态机,实际运行,才能够加深理解。

首先是状态机

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public enum CubeState
{
    Run,
    Idle,

}

public class CubeAI : MonoBehaviour
{
    FsmStateMachine<CubeAI> m_Machine;//状态机对象用来管理状态的
    public Dictionary<CubeState, FsmState<CubeAI>> StateDic; //状态字典,用来存放状态


    void Awake()
    {
        StateDic = new Dictionary<CubeState, FsmState<CubeAI>>();
        StateDic.Add(CubeState.Run, new CubeRun());
        StateDic.Add(CubeState.Idle, new CubeIdle());
    }

    // Use this for initialization
    void Start()
    {
        m_Machine = new FsmStateMachine<CubeAI>(this);

        m_Machine.SetCurrentState(StateDic[CubeState.Idle]);
    }

    // Update is called once per frame
    void Update()
    {
        m_Machine.SmUpdate();
    }

    public void ChangeAIState(CubeState state)
    {
        m_Machine.ChangeState(StateDic[state]);
    }
}

接着是休息 跑步的状态

using UnityEngine;
using System.Collections;

public class CubeIdle : FsmState<CubeAI>
{
    int idleTime = 0;
    float delateTime = 0;
    int i = 0;
    public override void Enter()
    {
        base.Enter();
        Debug.Log("Cube 进入了人生思考阶段");
        i = 0;
        delateTime = 0;
        idleTime = Random.Range(8, 16);
    }

    public override void Excute()
    {
        base.Excute();
        i++;
        Debug.Log("Cube 在思考人生" + i);
        delateTime += Time.deltaTime;
        if (delateTime >= idleTime)
        {
            Target.ChangeAIState(CubeState.Run);
        }
    }

    public override void Exit()
    {
        base.Exit();
        Debug.Log("Cube 结束了人生思考");
        i = 0;
    }

}

 

using UnityEngine;
using System.Collections;

public class CubeRun : FsmState<CubeAI>
{
    int runTime = 0;
    float delateTime = 0;
    public override void Enter()
    {
        base.Enter();
        Debug.Log("Cube 进入了人生思考阶段");
        runTime = Random.Range(5, 10);
        delateTime = 0;
    }

    public override void Excute()
    {
        base.Excute();
        Target.transform.Translate(Vector3.forward * 1 * Time.deltaTime);
        Debug.Log("Cube 快乐的奔跑中,跑到了" + Target.transform.position);
        delateTime += Time.deltaTime;
        if (delateTime >= runTime)
        {
            Target.ChangeAIState(CubeState.Idle);
        }
    }

    public override void Exit()
    {
        base.Exit();
        Debug.Log("Cube 跑类了");
    }
}

理解完可能需要一点时间,其实可以用到项目上了,无非就是状态的切换,然后在状态进入的时候,操作什么,状态进入后执行什么,状态退出后操作什么。状态机的作用就是让你清楚你自己的逻辑,不至于代码混乱,思维混乱,写代码最忌混乱,一乱BUG便会找上门。当你熟悉这个机制后,可以尝试的把自己的理解加进去,来充实,强壮这个状态机。当你熟悉之后,你会发现,它的用处无处不在。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值