LOFTER for ipad —— 让兴趣,更有趣

点击下载 关闭
Unity-有限状态机:实现
Zeiod 2022-05-14

有限状态机

 

实现

状态基类

有限状态机基类(后续角色的基类)


状态基类

有限状态机基类


代码:

状态基类

using System;

using System.Collections;

using System.Collections.Generic;

using UnityEngine;


/// <summary>

/// 状态对象基类:所有状态的基类

/// 之后所有状态都继承这个类

/// 如:Idle Walk Attack...

/// </summary>

public abstract class StateBase //抽象类,因为不需要直接实例化使用他,只需要别人继承

{

    //当前状态代表的枚举状态

    public Enum StateType;//状态类型


    //当前控制的角色

    public FSMControllerBase controller;


    //首次实例化时的状态初始化

    public virtual void Init(FSMControllerBase controller,Enum stateType)//虚方法:可以用,可以不用 // 参数传入当前代表的类型

    {

        this.StateType = stateType;

        this.controller = controller;

    }



    //一个状态一定会存在:进入 更新 退出


    //进入:进入之后做什么事情

    //比如原来是走路的,现在切换到待机。现在要播放待机动画

    public abstract void OnEnter(); //抽象方法,不能实现具体功能,继承的子类必须要有这个方法并且实现功能

    //更新:在状态过程中做什么事情

    //时刻看看有没有按键,按键了要切换状态,比如从待机切换到移动

    public abstract void OnUpdate();

    //退出:离开时要做什么事情

    public abstract void OnExit();

}

有限状态机基类

using System;

using System.Collections;

using System.Collections.Generic;

using UnityEngine;


/// <summary>

/// 有限状态机控制类

/// 使用的人是:玩家、怪物这样的角色

/// </summary>

public abstract class FSMControllerBase : MonoBehaviour //FSM 有限状态机(finite state machine)

{

    //当前的状态

    public abstract Enum CurrentState { get; set; }//定义为抽象的abstract,说明未来实际玩家或怪物的类需要去重写当前状态

    //因为未来我们是玩家的移动或怪我的攻击等实际枚举。这个地方现在是获取不到的,压根没有创建,也不知道会有多少个。

    //所以写成抽象以后,子类继承后一定要重写实现这个

    //实现的时候就可以制定一下是PlayerState还是MonsterState,来传给他。这样就可以运作了。


    //当前的状态对象

    protected StateBase CurrStateObj;


    //存放全部状态对象-对象池

    private List<StateBase> stateList = new List<StateBase>();//存放状态对象的列表


    /// <summary>

    /// //修改状态

    /// </summary>

    public void ChangeState(Enum newState,bool reCurrState=false) //参数传新状态,是否需要刷新当前状态

                                                                  //(默认刷新为false,因为大多数不需要,因为不需要跑步切换到跑步再切换到跑步)

    {

        //如果新状态和当前状态一致 同时 并不需要刷新状态,就什么都不用做

        if(newState==CurrentState && !reCurrState)

        {

            return;

        }

        //如果当前状态存在,应该执行其退出方法

        if(CurrentState!=null)

        {

            CurrStateObj.OnExit();


            //基于新状态的枚举,获取一个新的状态对象

            CurrStateObj = GetStateObj(newState);

            //状态初始化

            //tempStateObj.Init(this,newState)  ;   不应该用这个,用CurrStateObj.OnEnter();逻辑上初始化

            //Init只有首次实例化时候用,相当于构造函数

            CurrStateObj.OnEnter();

        }



    }

    /// <summary>

    /// 获取状态对象

    /// 参数给一个枚举,返回和这个枚举同名的类型的对象

    /// 保证不会返回null

    /// </summary>

    /// <returns></returns>

    private StateBase GetStateObj(Enum stateType)

    {

        //这里注意,不能每次切换状态都new一个状态

        //比如,待机到移动,移动到待机....来回切换。如果每次都new一个消耗很大

        //要避免没有用的东西多次实例化,尽可能重复来用。所以要有所有对象的对象池


        //看一下库里有没有

        for(int i=0;i<stateList.Count;i++)

        {

            //如果类型一致就可以使用

            if(stateList[i].StateType==stateType)

            {

                return stateList[i];

            }

        }

        //如果代码运行到这里说明库里没有

        //实例化一个并且返回

        //只有一个Enum stateType枚举类型的变量,怎么返回状态对象? 反射 :通过名称实例化一个实例

        StateBase state = Activator.CreateInstance(Type.GetType(stateType.ToString())) as StateBase;//创建实例方法,参数需要类型。 as StateBase 转换为StateBase类型

        //初始化状态,不然直接调用这个状态对象时也没有意义

        state.Init(this,stateType);

        //将这个状态添加到状态池

        stateList.Add(state);

        return state;

    }


    //状态过程中更新

    //重写时可调用base.Update()

    protected virtual void Update()//protected 子类可调用 virtual 子类可重写

    {

        if (CurrStateObj != null) CurrStateObj.OnUpdate();

    }


}


推荐文章
评论(0)
分享到
转载我的主页