Unity Game FrameWork—模块使用—有限状态机

官方说明: 提供创建、使用和销毁有限状态机的功能,一些适用于有限状态机机制的游戏逻辑,使用此模块将是一个不错的选择。
有限状态机并不是游戏中独有的,我们看一下其他的介绍:
有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。在计算机科学中,有限状态机被广泛用于建模应用行为、硬件电路系统设计、软件工程,编译器、网络协议、和计算与语言的研究。
[图片]
[图片]
图片来源:https://blog.csdn.net/DataAlchemist/article/details/98450010
参考上面两幅图,当玩家处于当前状态时,玩家会执行某些操作,当这些操作触发切换状态的条件时,则切换状态。如果用if else来判断则太过复杂不便于梳理流程,以及一堆条件变量控制,当状态改变或触发条件改变,整个流程的维护是很复杂的,我们不能专注当前状态要做的事,需要去看前后关联的触发条件是否会有问题,这些条件变量在其他状态是怎样的,会不会由于条件变量的改变导致其它问题,因此诞生了状态机。我们只需关注当前状态要干什么,干完你之后什么条件下会触发下一个状态。
前面有写过,流程是对状态机的封装,我们来理解一下流程的思路,首先进入游戏的初始状态完成版本构建、语言配置等工作即可触发Splash 动画流程展示,ProcedureSplash流程中选择资源加载模式,例如选择编辑器模式,则进入ProcedurePreload资源预加载流程。相关游戏资源加载完成后,则进入切换场景的流程ProcedureChangeScene。状态机所解决的问题与流程需要完成的事情是完全吻合的。
流程是对状态机做了一层封装,抽丝剥茧理解起来相对复杂,抓不住问题的本质,这里我们做一个简单地Demo来完成状态机学习。借用一下浅墨大哥写的设计模式和案例:(缅怀,人走了,知乎博客还在影响着我们)
【游戏设计模式】之三 状态模式、有限状态机 & Unity版本实现
状态机Demo:Behavioral Patterns-State Pattern-Exmaple4
接下来会参考这个案例用框架完成这个Demo的功能,Demo的流程图如下
[图片]

Demo工作流:

ProcedureFSM:流程类
FSM_Hero:英雄逻辑类
State:FsmState<FSM_Hero>状态类
创建ProcedureFSM流程类,在流程类中创建英雄实体并添加逻辑类FSM_Hero,英雄逻辑类中创建四种状态类,随后在四个状态类中完成触发及状态切换等工作。(流程启动,实体创建及数据表配置请参考本专栏之前的内容)
下面贴上代码:
下蹲状态:

public class DuckingState : FsmState<FSM_Hero>
{
    public override void OnEnter(IFsm<FSM_Hero> fsm)
    {
        Debug.Log("------------------------Heroine in DuckingState~!(进入下蹲躲避状态!)");
    }
    public override void OnUpdate(IFsm<FSM_Hero> fsm, float elapseSeconds, float realElapseSeconds)
    {
        HandleInput(fsm);
    }

    public void HandleInput(IFsm<FSM_Hero> fsm)
    {
        if (Input.GetKeyDown(KeyCode.DownArrow))
        {
            Debug.Log("已经在下蹲躲避状态中!");
            return;
        }
        if (Input.GetKeyUp(KeyCode.UpArrow))
        {
            Debug.Log("get GetKeyUp.UpArrow!");
            ChangeState<StandingState>(fsm);
        }
    }
}

站立状态:

public class StandingState : FsmState<FSM_Hero>
{
    public override void OnEnter(IFsm<FSM_Hero> fsm)
    {
        Debug.Log("------------------------Heroine in StandingState~!(进入站立状态!)");
    }
    public override void OnUpdate(IFsm<FSM_Hero> fsm, float elapseSeconds, float realElapseSeconds)
    {
        HandleInput(fsm);
    }

    public void HandleInput(IFsm<FSM_Hero> fsm)
    {
        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            Debug.Log("get KeyCode.UpArrow!");
            ChangeState<JumpingState>(fsm);
        }
        if (Input.GetKeyDown(KeyCode.DownArrow))
        {
            Debug.Log("get KeyCode.DownArrow!");
            ChangeState<DuckingState>(fsm);
        }
    }
}

下斩状态:

public class DrivingState : FsmState<FSM_Hero>
{
    public override void OnEnter(IFsm<FSM_Hero> fsm)
    {
        Debug.Log("------------------------Heroine in DrivingState~!(进入下斩状态!)");
    }
    public override void OnUpdate(IFsm<FSM_Hero> fsm, float elapseSeconds, float realElapseSeconds)
    {
        HandleInput(fsm);
    }

    public void HandleInput(IFsm<FSM_Hero> fsm)
    {
        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            Debug.Log("get KeyCode.UpArrow!");
            ChangeState<StandingState>(fsm);
        }
    }
}

跳跃状态:

public class JumpingState : FsmState<FSM_Hero>
{
    public override void OnEnter(IFsm<FSM_Hero> fsm)
    {
        Debug.Log("------------------------Heroine in JumpingState~!(进入跳跃状态!)");
    }
    public override void OnUpdate(IFsm<FSM_Hero> fsm, float elapseSeconds, float realElapseSeconds)
    {
        HandleInput(fsm);
    }

    public void HandleInput(IFsm<FSM_Hero> fsm)
    {
        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            Debug.Log("get GetKeyDown.UpArrow! but already in Jumping! return!(已经在跳跃状态中!)");
            return;
        }
        if (Input.GetKeyDown(KeyCode.DownArrow))
        {
            Debug.Log("get KeyCode.DownArrow!");
            ChangeState<DrivingState>(fsm);
        }
    }
}

英雄逻辑类

public class FSM_Hero : EntityLogic
{
    public override void OnInit(object userData)
    {
        base.OnInit(userData);

        FsmState<FSM_Hero>[] heroState =
        {
            new StandingState(),
            new DuckingState(),
            new JumpingState(),
            new DrivingState(),
        };
        var heroFsm = StarForce.GameEntry.Fsm.CreateFsm(this, heroState);
        heroFsm.Start<StandingState>();
    }

    public override void OnShow(object userData)
    {
        base.OnShow(userData);
    }
    
    public override void OnHide(bool isShutdown, object userData)
    {
        base.OnHide(isShutdown, userData);
        StarForce.GameEntry.Fsm.DestroyFsm<FSM_Hero>();
    }

    public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
    {
        base.OnUpdate(elapseSeconds, realElapseSeconds);
    }
}

Demo流程启动:

public class ProcedureFSM : ProcedureBase
{
    public override bool UseNativeDialog
    {
        get
        {
            return true;
        }
    }
    public override void OnEnter(ProcedureOwner procedureOwner)
    {
        base.OnEnter(procedureOwner);
        GameEntry.Entity.ShowEntity<FSM_Hero>(99999, "Assets/GameMain/Entities/Hero.prefab", "Aircraft");
    }
}

数据交互

状态机内部不同状态间通常需要数据交互,GetData,SetData,HasData,RemoveData四个接口提供了数据的获取、设置、是否存在、删除的功能。数据以key-value的形式存储于字典中。
框架对于数据做了一层封装,获取数据时,不用关心是什么类型,用统一的接口获取,强制转换。传入数据时,也可根据自己的需要自定义数据类型。
如下代码,是框架定义的数据类型,演示了四种接口的使用

VarBoolean a = new VarBoolean();
a.Value = true;
heroFsm.SetData("name", a);
g = (bool)heroFsm.GetData("name").GetValue();
if (heroFsm.HasData("name"))
{
    heroFsm.RemoveData("name");
}

自定义数据类型的两种写法如下,用bool类型做一个演示。
方式一:

public class Boolable : Variable
{
    public bool? value;
    public Boolable(bool? value = null)
    {
        this.value = value;
    }
    public override Type Type => value.GetType();

    public override void Clear()
    {
        value = null;
    }
    public override object GetValue()
    {
        return value;
    }
    public override void SetValue(object value)
    {
        this.value = (bool)value;
    }
}

方式二:

public class Boolean : Variable<bool>
{
    /// <summary>
    /// 初始化 System.Boolean 变量类的新实例。
    /// </summary>
    public Boolean()
    {
    }

    /// <summary>
    /// 从 System.Boolean 到 System.Boolean 变量类的隐式转换。
    /// </summary>
    /// <param name="value">值。</param>
    public static implicit operator Boolean(bool value)
    {
        Boolean varValue = ReferencePool.Acquire<Boolean>();
        varValue.Value = value;
        return varValue;
    }

    /// <summary>
    /// 从 System.Boolean 变量类到 System.Boolean 的隐式转换。
    /// </summary>
    /// <param name="value">值。</param>
    public static implicit operator bool(Boolean value)
    {
        return value.Value;
    }
}

两种方式没有本质上的区别,Variable是框架的抽象变量类,变量的类型用泛型表示,如果继承于该类,只需定义构造函数即可。如果对于SetData,GetData有其他的实现形式,可以直接继承于Variable。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值