天天看点

基于委托与事件的有限状态机设计与实现(Unity3d)有限状态机设计与实现

有限状态机设计与实现

前言

其实在游戏开发上,有限状态机应用的方面还是很多,虽然现在有更好的解决方案,比如行为树。但是简单的说,有限状态机更容易实现,在状态比较少的情况下,使用有限状态机显得更直观。但是在状态过多的时候,使用有限状态机就会使得状态变得十分复杂,但是我认为有限状态机在游戏开发上又是比较基础的东西,所以还是应该了解并掌握的。

什么是有限状态机?

有限状态机,(英语:Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

状态存储关于过去的信息,就是说:它反映从系统开始到现在时刻的输入变化。转移指示状态变更,并且用必须满足来确使转移发生的条件来描述它。动作是在给定时刻要进行的活动的描述。有多种类型的动作:

进入动作:在进入状态时进行

退出动作:在退出状态时进行

输入动作:依赖于当前状态和输入条件进行

转移动作:在进行特定转移时进行

我自己对状态机的理解就是,状态机负责状态的切换以及当前状态的动作。

那么这样的话就可以设计一个状态机负责角色的状态切换以及执行状态内的方法,多个状态类需要有状态的进入动作Enter,退出动作Exit以及执行的动作Action(转移动作以后再说),还有一个角色类,通过角色类的动作来触发状态机的状态切换来达到状态的切换。

设计状态机UML图

基于委托与事件的有限状态机设计与实现(Unity3d)有限状态机设计与实现

大概是这样的一个思路,这个图有点旧就凑合着先用着了。

状态机的设计思路

我是想游戏的AI与玩家都能共用一套状态机,这样会不会比较方便一些,所以就给Player类跟AIEnemy类都设计了一个他们的基类AllCharacter类,接着是状态机FiniteStateMachine类以及他们的状态基类BaseState类。

AllCharacter.cs

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

public class AllCharacter : MonoBehaviour
{
    [HideInInspector]
    public BaseState CurBaseState;

    public enum StateType
    {
        //AI状态
        STATE_PATROL,
        STATE_CHASE,
        //玩家状态
        STATE_STAND,
        STATE_MOVE,
        //共同状态
        STATE_ATTACK,
        STATE_DEAD,
    }

    public StateType CharaterState;
}
           

Player.cs

Player类中定义了一个委托PlayerStateChange和一个PlayerStateChange的事件StateChange,在Awake中注册属于Player的状态
以及注册状态切换事件的方法ChangeState(在FiniteStateMachine中)
           
public class Player : AllCharacter
{
    public delegate void PlayerStateChange(AllCharacter.StateType stateType);
    public event PlayerStateChange StateChange;

    [HideInInspector]
    public FiniteStateMachine FsMachine;

    // Use this for initialization
    void Awake ()
    {
        FsMachine = new FiniteStateMachine(this);
        //注册角色的状态
        FsMachine.RegisterState(new AttackState(this));
        FsMachine.RegisterState(new StandState(this));
        FsMachine.RegisterState(new DeadState(this));
        FsMachine.RegisterState(new MoveState(this));
        //注册事件
        StateChange+=new PlayerStateChange(FsMachine.ChangeState);
    }

    void Start()
    {
        //设置角色的初始状态
        EnterStand();
    }

    void Update () {
        FsMachine.OnUpdate();
	}

    public void StickMove()
    {
        Debug.Log("Moving the JoyStick");
        StateChange(AllCharacter.StateType.STATE_MOVE);
    }

    public void DownAttack()
    {
        Debug.Log("Down the Attack");
        StateChange(AllCharacter.StateType.STATE_ATTACK);
    }

    public void EnterStand()
    {
        StateChange(AllCharacter.StateType.STATE_STAND);
    }
    }
           

Player中的方法StickMove()、DownAttack()是通过EasyTouch操作调用的,通过这两个方法的调用可以控制角色状态的切换

BaseState.cs

public class BaseState
{
    //获取角色
    public AllCharacter Character;
    
    //得到当前状态
    public virtual AllCharacter.StateType GetStateType()
    {
        return Character.CharaterState;
    }
    //进入状态
    public virtual void EnterState(FiniteStateMachine fsMachine, BaseState preState)
    {

    }
    //状态更新
    public virtual void UpdateState()
    {

    }
    //状态结束 退出状态
    public virtual void ExitState(BaseState preState)
    {

    }
    //输入控制
    public virtual void InputHandle()
    {

    }

}
           

MoveState.cs

这是其中一个状态类,负责角色移动时的动作

public class MoveState : BaseState
{
    private readonly Player _player;
    public MoveState(Player player)
    {
        this._player = player;
    }
    public override AllCharacter.StateType GetStateType()
    {
        return AllCharacter.StateType.STATE_MOVE;
    }
    public override void EnterState(FiniteStateMachine fsMachine, BaseState preState)
    {
        if (preState != null)
        {
            Debug.Log("Enter the MoveState the preState is:" + preState.GetStateType());
        }
        else
        {
            Debug.Log("Enter the MoveState");
        }
        //Debug.Log(_player.CurBaseState);
    }

    public override void UpdateState()
    {
        Debug.Log("I'm Moving.");
    }

    public override void ExitState(BaseState preState)
    {
        Debug.Log("Exit Move State. " + preState.GetStateType());
    }

    public override void InputHandle()
    {

    }
}
           

总结

其实有限状态机蛮简单的,也很基础的东西,就是可以理解为Player在把在当前的状态类执行的方法告诉状态机,接着状态机根据这个方法帮Player切换Player的状态,接着再执行该状态类的方法。要是以上有说得不妥的地方,希望帮忙指正一下,谢谢。