【 unity3d 】FSM有限状态机(状态设计模式)

前言

为了解决游戏过于麻烦的状态转换(人物动画过多),使用有限状态机。
缺点:代码量大

编写

  1. 使用多态和Switch
  2. new(状态设计模式)

类的介绍

PlyayCtrl:挂载在角色身上的脚本,用来控制状态机,(控制状态机中状态的切换)。
Machine:状态机器类,储存所有状态,对状态进行切换,和状态的保持工作。
Base: 状态的基类,给子类提供接口(方法)
Template:泛型类当前状态的拥有者
Idle Run Attack …等各个具体状态的实现(播放动画)
1

各个类的关系图介绍

状态模式

脚本代码

1.状态基类

/// <summary>
/// 状态的基础类:给子类提供方法
/// </summary>
/// 
public class StateBase {

    //给每个状态设置一个ID
    public int ID{get;set;}

    //被当前机器所控制
    public StateMachine machine;

    public StateBase(int id)
    {
        this.ID = id;
    }

    //给子类提供方法
    public virtual void OnEnter(params object[] args){}
    public virtual void OnStay(params object[] args){}
    public virtual void OnExit(params object[] args){}

}

2.状态拥有者类(泛型类)

/// <summary>
/// 状态拥有者
/// </summary>
public class StateTemplate<T> : StateBase {

    public T owner;   //拥有者(范型)

    public StateTemplate(int id,T o):base(id)
    {
        owner = o;
    }
}

3.玩家脚本类

/// <summary>
/// PlayerCtrl
/// 挂载在角色身上的脚本,用来控制状态机器类
/// </summary>
/// 
public enum PlayerState
{
    None,
    Idle,
    Run,
    Attack
}

public class Player : MonoBehaviour {

    public Animation ani;
    public PlayerState ps = PlayerState.Idle;

    //控制机器
    public StateMachine machine;

    void Start()
    {
        ani = GetComponent<Animation> ();

        IdleState idle = new IdleState (1, this);
        RunState run = new RunState (2, this);
        AttackState attack = new AttackState (3, this);

        machine = new StateMachine (idle);
        machine.AddState (run);
        machine.AddState (attack);
    }

    void Update()
    {
        if (Input.GetMouseButtonDown (0)) {
            ps = PlayerState.Attack;
        }
        if (Input.GetKey (KeyCode.A)) {
            ps = PlayerState.Run;
        }
        if (Input.GetKeyUp (KeyCode.A)) {
            ps = PlayerState.Idle;
        }

        //根据枚举 让状态机器类去切换状态
        UpdateAnimation ();
    }
    private void UpdateAnimation()
    {
        switch (ps) {
        case PlayerState.Idle:
            machine.TranslateState (1);
            break;
        case PlayerState.Run:
            machine.TranslateState (2);
            break;
        case PlayerState.Attack:
            machine.TranslateState (3);
            break;
        }
    }

    void LateUpdate()
    {
        machine.Update ();
    }
}

4.状态机类

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

/// <summary>
/// 状态机器类:由Player控制。完成状态的存储,切换,和状态的保持
/// </summary>

public class StateMachine {

    //用来存储当前机器所控制的所有状态
    public Dictionary<int,StateBase> m_StateCache;

    //定义上一个状态
    public StateBase m_prviousState;
    //定义当前状态
    public StateBase m_currentState;

    //机器初始化时,没有上一个状态
    public StateMachine(StateBase beginState)
    {
        m_prviousState = null;
        m_currentState = beginState;

        m_StateCache = new Dictionary<int, StateBase> ();
        //把状态添加到集合中
        AddState (beginState);
        m_currentState.OnEnter ();
    }

    public void AddState(StateBase state)
    {
        if (!m_StateCache.ContainsKey (state.ID)) {
            m_StateCache.Add (state.ID, state);
            state.machine = this;
        }
    }

    //通过Id来切换状态
    public void TranslateState(int id)
    {
        if (!m_StateCache.ContainsKey (id)) {
            return;
        }

        m_prviousState = m_currentState;
        m_currentState = m_StateCache[id];
        m_currentState.OnEnter ();
    }

    //状态保持
    public void Update()
    {
        if (m_currentState != null) {
            m_currentState.OnStay ();
        }
    }
}

5.人物普通状态类

/// <summary>
/// Idle状态
/// </summary>
public class IdleState : StateTemplate<Player> {

    public IdleState(int id,Player p):base(id,p){
    }

    public override void OnEnter (params object[] args)
    {
        base.OnEnter (args);
        owner.ani.Play ("Idle");
    }
    public override void OnStay (params object[] args)
    {
        base.OnStay (args);
    }
    public override void OnExit (params object[] args)
    {
        base.OnExit (args);
    }
}

6.跑步状态类

using UnityEngine;
using System.Collections;

/// <summary>
/// Run状态
/// </summary>

public class RunState : StateTemplate<Player> {

    public RunState(int id,Player p):base(id,p){
    }

    public override void OnEnter (params object[] args)
    {
        base.OnEnter (args);
        owner.ani.Play ("Run");
    }
    public override void OnStay (params object[] args)
    {
        base.OnStay (args);
    }
    public override void OnExit (params object[] args)
    {
        base.OnExit (args);
    }
}

7.攻击状态类

using UnityEngine;
using System.Collections;

/// <summary>
/// Attack状态
/// </summary>
public class AttackState : StateTemplate<Player> {

    public AttackState(int id,Player p):base(id,p){
    }

    public override void OnEnter (params object[] args)
    {
        base.OnEnter (args);
        owner.ani.Play ("Attack");
    }
    public override void OnStay (params object[] args)
    {
        base.OnStay (args);
        if (!owner.ani.IsPlaying ("Attack")) {
            OnExit ();
        }
    }
    public override void OnExit (params object[] args)
    {
        base.OnExit (args);
        owner.ps = PlayerState.Idle;
    }
}

总结

通过即只添加代码,不删除代码的设计原则来写有限状态机。
FSM简单来说就是
4个类
1. 状态基类 状态基类需要3个虚方法,以及虚拟机对象
2. 拥有者泛型类 继承状态基类,构造出使用者对象
3. 玩家脚本类
在Start方法里构造new各个状态机类(传入枚举id,使用者this(自己)),new状态机对象,添加各个状态类对象。
在Update通过按键,并一直调用状态机的切换方法。
在LateUpdate()调用状态机的保持状态方法
4. 状态机类
包含添加状态类,切换状态类方法(控制Enter()),保存状态类方法(控制(Stay()))
加若干个状态类
每个状态具体有3种状态方法(OnEnter,OnStay,OnExit)需要重写来具体实现,通过OnExit()方法改变使用者的枚举id来切换状态

猜你喜欢

转载自blog.csdn.net/liaoshengg/article/details/81014770