Unity进阶开发-FSM有限状态机

这篇具有很好参考价值的文章主要介绍了Unity进阶开发-FSM有限状态机。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

# Unity进阶开发-FSM有限状态机

前言

我们在进行开发时,到了一定程度上,会遇到数十种状态,继续使用Unity的Animator控制器会出现大量的bool,float类型的变量,而这些错综复杂的变量与Animatator控制器如同迷宫版连线相结合会变得极其的复杂且无法良好维护扩展,出现一个BUG会导致开发过程中开发者承受极大的精神力,而这时候,使用有限状态机或者AI行为树便成为了一个极佳的选择,本文只记录了有限状态机的开发

使用有限状态机进行状态管理与切换可以大幅度的减少开发时候的难度,在开发过程中只需要关注各个状态间的切换即可

图示FSM工作过程:

Unity进阶开发-FSM有限状态机

我们可以看到,FSM的脚本一共分为两大块儿,一块儿继承自IState,这里面保存着状态内执行的行为,例如进入状态,离开状态,逻辑切换(用于判断是否切换到其他状态),PlayerState实现这个接口,并继承ScriptableObject类,看到ScriptableObject我们就知道了PlayerState的作用了,ScriptableObject类的对象不依赖场景中的对象可,是独立存在的数据文件。而在这里,PlayState保存的是状态的行为逻辑函数,这些行为逻辑函数的本质作用就是满足条件就去执行相关行为。当然,在FSM中用一个例子来说明,当玩家处于跑步状态时,PlayState文件发现满足执行跑步行为的逻辑,就需要去播放跑步动画。我们知道,ScriptableObject的数据是不会自己变化的,那么所有判断逻辑只能使用一遍,我们该如何解决这个问题,这就需要FSM第二的板块儿的类来实现了

我们不难发现,StateMachine是继承MonoBehaviour的,这就需要挂载到场景中了,并且也可以使用Update函数来时刻改变检测到的信息,那么,上文已经说了,PlayerState的数据不能主动变化,而StateMachine的数据可以进行变化,所以我们就可以通过一个函数来讲StateMachine中检测到数据来发给PlayerState,以此来实现PlayerState创建的数据文件可以连续进行逻辑判断

好了,现在开始上代码,用代码继续说

No.1 IState

public interface IState
{
    //进入状态
    void Enter() { }
    //退出状态
    void Exit() { }
    //状态逻辑更新
    void LogicUpdate() { }
}

在这里就是状态内部需要执行的行为函数

No.2 StateMachine

// <summary>
/// 持有所有状态类,并对他们进行管理和切换
/// 负责当前状态的更新
/// </summary>
public class StateMachine : MonoBehaviour
{
    IState currentState;                                           //当前状态
    public Dictionary<System.Type, IState> stateTable;             //字典,用于保存状态以及查询状态,方便状态切换时                                                                    //进行查找
    public void Update()
    {
        currentState.LogicUpdate();                                //执行每个状态的逻辑切换函数,可以使状态实现检测                                                                    //变换,类似于Update内的函数实时接收信息
    }
    //切换状态
    protected void SwitchOn(IState newState)
    {
        //当前状态变为新状态
        currentState = newState;
        //进入新状态
        currentState.Enter();
    }
    //
    public void SwitchState(IState newState)
    {
        //退出状态
        currentState.Exit();
        //进入新状态
        SwitchOn(newState);
    }
    //切换状态函数重载
    public void SwitchState(System.Type newStateType)
    {
        SwitchState(stateTable[newStateType]);                    //将字典内的状态传入
    }

}

No.3 PlayetState

public class PlayerState : ScriptableObject, IState
{
    /*************物理检测*************/
    protected bool isGround;                          //是否在地面
    /*************基础信息*************/
    protected bool isRun;                             //是否跑步
    protected bool isJump;                            //是否跳跃
    protected bool isIdle;                            //是否静止
    /*************相关组件*************/
    protected Rigidbody2D my_Body2D;                  //刚体组件,用于获取物体刚体属性
    protected Animator animator;                      //动画组件,用来播放动画
    protected PlayerStateMachine stateMachine;        //PlayerStateMachine,玩家状态机类,执行状态间的切换
    public void Initiatize(Animator animator, Rigidbody2D my_Body2D, PlayerStateMachine stateMachine)
    {//获取PlayerStateMachine传递进来的 动画,刚体,状态机类
        this.animator = animator;
        this.my_Body2D = my_Body2D;
        this.stateMachine = stateMachine;
    }
    public void PhysicalDetection(bool isGround)
    {
        this.isGround = isGround;
    }
    /// <summary>
    /// 状态信息传递
    /// </summary>
    public void BasicInformation(bool isRun,bool isJump,bool isIdle)
    {
        this.isRun = isRun;
        this.isJump = isJump;
        this.isIdle = isIdle;
    }
    //进入状态
    public virtual void Enter() { }
    //离开状态
    public virtual void Exit() { }
    //逻辑切换
    public virtual void LogicUpdate() { }
}

No.4 PlayerStateMachine

/// <summary>
/// 玩家状态机类
/// </summary>
public class PlayerStateMachine : StateMachine
{
    /*************************检测信息***************************/
    PlayerPhysicalDetection playerPhysicalDetection;                      //物理检测组件,这个是继承                                                                                       //MonoBehaviour,挂载到玩家身上来检测                                                                           //玩家的物理信息,例如是否位于地面
    /*************************状态信息***************************/
    //PlayerState资源文件
    [SerializeField] PlayerState[] states;
    Animator animator;                            //获取动画组件
    Rigidbody2D my_Body2D;                        //获取刚体组件
    void Awake()
    {
        /*************************物理检测信息***************************/
        playerPhysicalDetection=GetComponent<PlayerPhysicalDetection>();            //获取物理检测组件

        /*************************状态信息组件***************************/
        stateTable = new Dictionary<System.Type, IState>(states.Length);            //初始化字典
        animator = GetComponent<Animator>();                                        //获取动画组件
        my_Body2D = GetComponent<Rigidbody2D>();                                    //获取刚体组件
        //迭代器循环获取状态
        foreach (PlayerState state in states)
        {
            state.Initiatize(animator, my_Body2D, this);//将动画组件,刚体组件以及PlayerStateMachine传入进去
            //状态存入字典
            stateTable.Add(state.GetType(), state);
        }
    }
    private void Start()
    {//在开始时执行Idle,进入Idle状态
        SwitchOn(stateTable[typeof(PlayerState_Idle)]);
    }
    private new void Update()
    {
        base.Update();//执行父类StateMachine的Update函数
        foreach (PlayerState state in states)
        {//将检测信息传入进去
            state.PhysicalDetection(playerPhysicalDetection.isGround);
            state.BasicInformation( isRun, isJump, isIdle);
        }
    }
}

No.5 PlayerState_Idle

[CreateAssetMenu(menuName = "StateMachine/PlayerState/Idle", fileName = "PlayerState_Idle")]//创建文件
public class PlayerState_Idle : PlayerState
{
    /*****************物理检测*******************/
    public override void Enter()
    {
        //执行该状态数据文件,首先执行进入状态函数,在进入状态函数执行相关的行为
        //进入状态,默认播放Idle动画
        animator.Play("PlayerIdle");
    }
    
    //逻辑切换函数,当检测到处于某种状态,立刻执行该状态的数据文件
    public override void LogicUpdate()
    {
        if(isRun)
        {
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Run)]);
        }
        if(isJump)
        {
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Jump)]);
        }
        if(my_Body2D.velocity.y<0&&!isGround)
        {
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Fall)]);
        }
    }
}

No.6 PlayerState_Jump

[CreateAssetMenu(menuName = "StateMachine/PlayerState/Jump", fileName = "PlayerState_Jump")]//创建文件
public class PlayerState_Jump : PlayerState
{
    /*****************物理检测*******************/
    public override void Enter()
    {
        //执行该状态数据文件,首先执行进入状态函数,在进入状态函数执行相关的行为
        //进入状态,默认播放Idle动画
        animator.Play("PlayerJump");
    }
    
    //逻辑切换函数,当检测到处于某种状态,立刻执行该状态的数据文件
    //该脚本中,跳跃后无法执行其它行为,若有需要可以添加判断
    public override void LogicUpdate()
    {
        if(my_Body2D.velocity.y<0&&!isGround)
        {
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Fall)]);
        }
    }
}

No.7 PlayerState_Fall

[CreateAssetMenu(menuName = "StateMachine/PlayerState/Fall", fileName = "PlayerState_Fall")]//创建文件
public class PlayerState_Jump : PlayerState
{
    /*****************物理检测*******************/
    public override void Enter()
    {
        //执行该状态数据文件,首先执行进入状态函数,在进入状态函数执行相关的行为
        //进入状态,默认播放Fall动画
        animator.Play("PlayerFall");
    }
    
    //逻辑切换函数,当检测到处于某种状态,立刻执行该状态的数据文件
    //该脚本中,掉落时无法执行其它行为,若有需要可以添加判断
    public override void LogicUpdate()
    {
        if(isGround)
        {//落地进入Idle状态
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Idle)]);
        }
    }
}

No.8 PlayerState_Run

[CreateAssetMenu(menuName = "StateMachine/PlayerState/Run", fileName = "PlayerState_Run")]//创建文件
public class PlayerState_Idle : PlayerState
{
    /*****************物理检测*******************/
    public override void Enter()
    {
        //执行该状态数据文件,首先执行进入状态函数,在进入状态函数执行相关的行为
        //进入状态,默认播放Run动画
        animator.Play("PlayerRun");
    }
    
    //逻辑切换函数,当检测到处于某种状态,立刻执行该状态的数据文件
    public override void LogicUpdate()
    {
        if(isIdle)
        {
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Idle)]);
        }
        if(isJump)
        {
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Jump)]);
        }
        if(my_Body2D.velocity.y<0&&!isGround)
        {
            stateMachine.SwitchState(stateMachine.stateTable[typeof(PlayerState_Fall)]);
        }
    }
}

以上便是一个简单的状态机

总结与拓展延伸

FSM可以大幅减少玩家动画间的判断和切换,只需要关注一个状态到另一个状态的切换条件,满足条件就切换,不满足就继续执行

FSM在玩家上的使用并不明显,因为在这上面,进入某种状态后,我们只播放了动画,并没有执行其他行为。当我们用在怪物AI中会更加的方便,例如怪物处于巡逻状态,播放巡逻动画,执行巡逻的脚本文件(建立一个类,专门用来存怪物AI个状态行为函数,通过传递的方式将该类对象传递给EnemyState,由EnemyState进行操作),怪物处于追击玩家状态,播放追击动画,并执行追击玩家的行为函数,在怪物AI中,FSM的应用会更加轻松的管理怪物,怪物可以放技能了,就进入技能状态,发现玩家就进入追击玩家,一个条件一个行为,更加有利于FSM进行管理

补充

下面是我自己写的一个EnemyAI结构图示,这个示例用来展示FSM在怪物AI中的大致应用(不是项目版本,一个大概体现)
Unity进阶开发-FSM有限状态机文章来源地址https://www.toymoban.com/news/detail-711351.html

到了这里,关于Unity进阶开发-FSM有限状态机的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何基于FSM有限状态机实现Enemies AI

    本文简单介绍如何基于FSM有限状态机实现Enemies AI,首先定义敌人的AI逻辑:默认状态下Enemy为巡逻状态,有若干巡逻点位,Enemy在这些点位之间来回巡逻走动,同时检测Player的位置,当Player进入一定范围内时,Enemy进入寻路状态,寻路到Player位置前,进入Attacking攻击状态,当

    2023年04月09日
    浏览(38)
  • Go中的有限状态机FSM的详细介绍 _

    1、FSM简介 1.1 有限状态机的定义 有限状态机(Finite State Machine,FSM)是一种数学模型,用于描述系统在不同状态下的行为和转移条件。 状态机有三个组成部分: 状态(State)、事件(Event)、动作(Action) ,事件(转移条件)触发状态的转移和动作的执行。动作的执行不是必

    2024年02月12日
    浏览(69)
  • C# Unity FSM 状态机

    使用状态机可以降低代码耦合性,并且可以优化代码可读性,方便团队协作等。 对于游戏开发内容来讲游戏开发的流程控制玩家动画都可以使用FSM有限状态机来实现。 每个状态的基类,泛型参数表示所拥有者 有限状态机的接口 有限状态机管理器接口 有限状态机的基类 状态

    2024年02月09日
    浏览(48)
  • Unity有限状态机

    一、引言 在游戏开发中,经常会遇到游戏角色或实体具有多种状态,并且在不同状态之间需要切换的情况。例如,一个角色可能处于行走、奔跑、跳跃等不同的状态,并且根据玩家的输入或游戏逻辑,在这些状态之间进行切换。为了管理这些状态及其之间的转换,我们可以使

    2024年02月03日
    浏览(51)
  • 在Unity中实现有限状态机

    本文将介绍Unity开发中的有限状态机,给出对应的实现代码。 有限状态机借鉴了图灵机的思想,可以看作是最简单的图灵机。 它包含4要素: 现态 条件 动作 次态 基础的有限状态机不复杂,无非是几个状态定义成类,提供OnEnter/OnExit/OnUpdate方法,这里直接根据需求给出对应的

    2024年02月05日
    浏览(50)
  • unity有限状态机和模糊状态机(怪物AI、自动寻路)

    自动寻路步骤: 1、把场景中不同的物体勾选static 2、烘培寻路网格 3、添加NavMeshAgent组件 4、给需要寻路的物体添加脚本 游戏中有限状态机的体现:小怪的巡逻和追逐功能 模糊状态机的体现:当玩家离小怪比较近时,小怪会追逐玩家,当玩家离小怪比较远时小怪会停止追逐玩

    2024年02月04日
    浏览(54)
  • Unity | 渡鸦避难所-6 | 有限状态机控制角色行为逻辑

    有限状态机(英语:finite-state machine,缩写:FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型 在游戏开发中应用有限状态机,能够将复杂的行为逻辑分解为一组简单的状态和转换规则,每个状态都可以独立地处理其逻辑,使代码

    2024年01月16日
    浏览(44)
  • Unity通用有限状态机的从零搭建手册(一):阵前磨枪

    前言 本文的创作目的是总结工作中累积的经验和成果,并通过写教学文章的方式进行巩固和查漏补缺,在不足之处加以改进,工作时由于时间原因没有实现的一些设计也会借这次机会进行补完。我会在写文章的过程中和读者一起从新开始搭建和优化一个状态机系统以及一个应

    2024年02月02日
    浏览(46)
  • Spring状态机(FSM),让订单状态流转如丝般顺滑

    在复杂的应用程序设计中,尤其是那些涉及多个状态变迁和业务流程控制的场景,有限状态机( Finite State Machine, FSM )是一种强大而有效的建模工具。Spring框架为此提供了Spring状态机( Spring State Machine )这一组件,它允许开发者以一种声明式且结构清晰的方式来管理和控制对

    2024年03月12日
    浏览(51)
  • 前端状态管理与有限状态机

    当下前端流行的框架,都是用状态来描述界面(state = view),可以说前端开发实际上就是在维护各种状态(state),这已经成为目前前端开发的共识。 复制代码 View = ViewModel(Model); 理想情况下,ViewModel 是纯函数,给定相同的 Model,产出相同的 View。 state = view 很好理解,但如何在 vi

    2024年03月15日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包