Unity-状态机

使用场景:满足一定的条件就从某一个状态切换到指定状态

主要思想:一个脚本,两个普通类。脚本负责状态的切换和执行,一个类为状态,一个类为条件。

1、可以先做两个全局变量,用于给各类脚本/条件命名,方便查看

public enum FSMStateID
{
    None,//无状态
    Idle,//待机
    Dead,//死亡
    Pursuit,//追逐
    Attacking,//攻击
    Default,//默认
    Patrolling//巡逻
}

public enum FSMTriggerID
{
    NoHealth,//生命为0
    SawTarget,//发现目标
    ReachTarget,//目标进入攻击范围
    LoseTarget,//丢失目标
    CompletePatrol,//完成巡逻
    KilledTarget,//击杀目标
    WithoutAttackRange//目标离开/不在攻击范围
}

2、条件类,负责存放状态转换的条件,只需要提供一个输出为bool类型的函数

///<summary>
///条件类
///<summary>
public abstract  class FSMTrigger
{
    //ID用于命名该类
    public FSMTriggerID TriggerID { get; set; }
    /// <summary>
    /// 必须做初始化,写Id
    /// </summary>
    public FSMTrigger()
    {
        Init();
    }
    public abstract void Init();
    //必须实现的抽象类方法,传入脚本是为了方便数据的调用
    public abstract bool HandleTrigger(FSMBase fsm); 
}

举一个例子:血量没有的条件

public class NoHealthTrigger : FSMTrigger
{
    //利用传入的脚本获取数据
    public override bool HandleTrigger(FSMBase fsm)
    {
        return fsm.characterBasic.blood <= 0;
    }

    public override void Init()
    {
        TriggerID = FSMTriggerID.NoHealth;
    }
}

3、状态类,需要对外提供进入状态、状态内的执行、退出状态、判断条件的方法。

有一个字典用于存放,条件和满足该条件会进入的状态(这个字典需要在脚本中配置),所以还需要对外提供一个增加字典词条的函数

再有一个列表用于存放所有条件类,用于状态内的判断。

public abstract class FSMState
{
    public FSMStateID StateID;
    //存放条件以及对应的状态
    private Dictionary<FSMTriggerID, FSMStateID> map;
    //用于存放相关条件
    private List<FSMTrigger> Triggers;
    public abstract void Init();
    //初始化
    public FSMState() 
    {
        Init();
        map = new Dictionary<FSMTriggerID, FSMStateID> ();
        Triggers = new List<FSMTrigger> ();
    }
    
    //对外提供的增加字典词条的方法
    public void AddMap (FSMTriggerID triggerID, FSMStateID stateID)
    {
        map.Add (triggerID, stateID);
        //词条没增加一条就用反射将条件类放入列表
        Type type = Type.GetType ("FSM."+triggerID.ToString()+"Trigger");
        FSMTrigger trigger =Activator.CreateInstance(type) as FSMTrigger;
        Triggers.Add(trigger);
    }

    //进入、离开、执行的方法
    public virtual void EnterState(FSMBase fsm) { }
    public virtual void ExitState(FSMBase fsm) { }
    public virtual void ActionState(FSMBase fsm) { }

    //用于判断是否满足条件
    public void Reason(FSMBase fsm)
    {
       for (int i = 0; i < Triggers.Count; i++)
        {
            if (Triggers[i].HandleTrigger(fsm))
            {
                FSMStateID stateID = map[Triggers[i].TriggerID];
                fsm.ChangeActiveState(stateID);
                //切换状态
                return;
            }
        }
    }
}

举两个例子死亡和待机

//死亡状态
public class DeadState : FSMState
{
    public override void Init()
    {
        StateID = FSMStateID.Dead;
    }
    public override void EnterState(FSMBase fsm)
    {
        base.EnterState(fsm);
        fsm.enabled = false;
    }
}
//待机状态
public class IdleState:FSMState
{
    public override void Init()
    {
        StateID = FSMStateID.Idle;
    }
    public override void EnterState(FSMBase fsm)
    {
        base.EnterState(fsm);   
        //可以放一个播动画的
    }
    public override void ExitState(FSMBase fsm)
    {
        base.ExitState(fsm);
    }
}

4、控制脚本:用于调用状态的方法和提供状态切换的方法。我这里将一些数据的导入也做在这个脚本中了也可以分开写。

public class FSMBase : MonoBehaviour
{
    //存放所有状态需要手动创建
    private List<FSMState> states;
    [Tooltip("默认状态ID")]
    public FSMStateID defaultStateID;
    //用于存放当前状态
    private FSMState currentState;
    //保存默认的状态
    private FSMState defaultState;

    #region 查找数据
    [HideInInspector]
    public CharacterBasic characterBasic;
    private void InitComponent()
    {
        characterBasic = GetComponent<CharacterBasic>();
    }
    #endregion

    private void Start()
    {
        //找数据
        InitComponent();
        手动初始化
        ConfigFSM();
        //执行默认的状态
        InitDefualtState();
    }
    private void ConfigFSM()
    {
        //将现有的状态类加入list,并把对应的条件和状态存入对应状态类中
        states = new List<FSMState>();
        IdleState idle = new IdleState();
        idle.AddMap(FSMTriggerID.NoHealth, FSMStateID.Dead);
        states.Add(idle);
        DeadState dead = new DeadState();
        states.Add(dead);
    }
    private void InitDefualtState()
    {
        执行默认状态
        defaultState = states.Find(s=> s.StateID == defaultStateID);
        currentState = defaultState;
        currentState.EnterState(this);
    }
    public void Update()
    {
        currentState.Reason(this);//检测
        currentState.ActionState(this);
    }
    //对外提供改变当前状态的方法
    public void ChangeActiveState(FSMStateID stateID)
    {
        currentState.ExitState(this);
        currentState = stateID== FSMStateID.Default ? defaultState : states.Find(s => s.StateID == stateID);
        currentState.EnterState(this);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值