状态基类
有限状态机基类(后续角色的基类)
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();
}
}