FSM有限状态机的使用

devtools/2024/10/23 10:06:45/

解释

有限状态机(Finite State Machine, FSM)管理器,主要包括状态机本身(StateMachine类)和状态基类(StateBase类)。这个设计提供了一种灵活的方式来管理对象的状态切换,使对象在不同状态下可以表现出不同的行为。

主要功能和类结构

  1. StateMachine类(有限状态机控制器)
    • 管理和控制状态的切换,负责进入和退出不同的状态。
    • 每个状态对应一个继承自StateBase的状态类,这些状态类可以实现具体的逻辑。
    • 使用对象池技术,通过对象池管理状态实例,提升性能。
  2. StateBase类(状态基类)
    • 所有状态类的基类,定义了状态的生命周期方法,如初始化(Init)、进入状态(Enter)、退出状态(Exit)等。
    • 可以通过重写这些方法为不同状态实现具体行为。

详细方法说明

StateMachine类:
  1. Init(IStateMachineOwner owner)

    • 初始化状态机,绑定宿主对象,宿主实现IStateMachineOwner接口。
  2. ChangeState<T>(int newStateType, bool reCurrstate = false)

    • 切换到指定的新状态。
    • 如果新状态和当前状态一致,且不强制重新进入当前状态(reCurrstate=false),则不进行状态切换。
    • 调用Exit方法退出当前状态,调用Enter方法进入新状态,并注册新状态的UpdateLateUpdateFixedUpdate回调函数到MonoMgr
  3. GetState<T>(int stateType)

    • 从对象池中获取或创建一个状态实例,并初始化状态。
    • 如果该状态之前没有被创建,使用对象池创建新的状态,并初始化它(调用StateBase.Init)。
  4. Stop()

    • 停止状态机,退出当前状态并清理所有状态。
    • 清除所有状态字典中的状态,并调用UnInit方法处理状态的反初始化(回收对象)。
  5. Destroy()

    • 销毁状态机。释放所有引用并将状态机对象推回到对象池中。
  6. ResetInfo()

    • 重置状态机的基本信息(当前状态等),不涉及核心逻辑,仅为接口的一部分。

StateBase类:
  1. Init(IStateMachineOwner owner, int stateType, StateMachine stateMachine)

    • 初始化状态,在状态第一次创建时调用,绑定状态机和宿主。
  2. UnInit()

    • 当状态不再使用时,进行反初始化,主要是释放资源并将对象推回到对象池。
  3. Enter()

    • 状态进入时调用,每次状态切换都会调用该方法。子类可以重写以实现状态的进入逻辑。
  4. Exit()

    • 状态退出时调用,子类可以重写以实现退出逻辑。
  5. Update()

    • 状态更新逻辑。可以重写这个方法来为每一帧实现自定义的更新行为。
  6. LateUpdate()

    • 状态的LateUpdate方法,可以在子类中重写,实现后期更新逻辑。
  7. FixedUpdate()

    • 状态的FixedUpdate方法,可以在子类中重写,实现物理更新逻辑。
  8. ResetInfo()

    • 该方法不包含实际逻辑,仅作为对象池中重置信息的一部分。

示例案例:角色移动状态机

假设我们在一个游戏中为角色设计了移动和攻击两个状态。

1. 定义宿主类,实现IStateMachineOwner接口:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class StateCharacter : MonoBehaviour,IStateMachineOwner
{private StateMachine stateMachine;private StateCharacterState currentState;private void Awake(){stateMachine = new StateMachine();stateMachine.Init(this);stateMachine.ChangeState<StateCharacter_IdleState>(0);}private void Update(){if (Input.GetKeyUp(KeyCode.W)){stateMachine.ChangeState<StateCharacter_MoveState>(1);}if (Input.GetMouseButtonDown(0)){stateMachine.ChangeState<StateCharacter_AttackState>(2);}}
}

2. 定义状态类(IdleStateMoveStateAttackState):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class StateCharacterStateBase : StateBase
{protected StateCharacter stateCharacter;public override void Init(IStateMachineOwner owner, int stateType, StateMachine stateMachine){base.Init(owner, stateType, stateMachine);stateCharacter = (StateCharacter)owner;}
}using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class StateCharacter_IdleState : StateCharacterStateBase
{public override void Enter(){Debug.Log("进入Idle状态");}public override void Update(){Debug.Log("执行Idle状态");}public override void Exit(){Debug.Log("退出Idle状态");}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class StateCharacter_AttackState : StateCharacterStateBase
{public override void Enter(){Debug.Log("进入Attack状态");}public override void Update(){Debug.Log("执行Attack状态");}public override void Exit(){Debug.Log("退出Attack状态");}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class StateCharacter_MoveState : StateCharacterStateBase
{public override void Enter(){Debug.Log("进入Move状态");}public override void Update(){Debug.Log("执行Move状态");}public override void Exit(){Debug.Log("退出Move状态");}
}
3. 切换状态:
  • 当游戏角色按下W键时,状态机会切换到MoveState,角色会执行移动逻辑。
  • 当点击鼠标左键时,状态机会切换到AttackState,角色会执行攻击动作。

在这个过程中,状态机会调用EnterUpdateExit方法,来管理角色在不同状态下的行为。


每个公有方法的应用总结

  1. Init: 初始化状态机,将宿主传递给状态机。
  2. ChangeState: 切换到新的状态,如移动、攻击、闲置等。
  3. Stop: 停止状态机,清理当前和缓存的所有状态(如游戏暂停或重置时)。
  4. Destroy: 销毁状态机,释放状态机占用的资源。
  5. GetState: 从对象池中获取新的状态对象。
  6. ResetInfo: 可用于复位状态机的当前状态(没有实际逻辑)。

通过这套状态机管理系统,可以方便地管理角色或游戏对象在不同状态下的行为逻辑。

注:第二种使用方法

将StateMachine在包一层

1. 定义宿主类,实现IStateMachineOwner接口:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class StateCharacter : MonoBehaviour,IStateMachineOwner
{private StateMachine stateMachine;private StateCharacterState currentState;private void Awake(){stateMachine = new StateMachine();stateMachine.Init(this);ChangeState(StateCharacterState.Idle);}public void ChangeState(StateCharacterState state, bool reCurrent = false){currentState = state;switch (state){case StateCharacterState.Idle:stateMachine.ChangeState<StateCharacter_IdleState>((int)state, reCurrent);break;case StateCharacterState.Move:stateMachine.ChangeState<StateCharacter_MoveState>((int)state, reCurrent);break;case StateCharacterState.Attack:stateMachine.ChangeState<StateCharacter_AttackState>((int)state, reCurrent);break;}}
}

2. 定义状态类(IdleStateMoveStateAttackState):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class StateCharacterStateBase : StateBase
{protected StateCharacter stateCharacter;public override void Init(IStateMachineOwner owner, int stateType, StateMachine stateMachine){base.Init(owner, stateType, stateMachine);stateCharacter = (StateCharacter)owner;}
}using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class StateCharacter_IdleState : StateCharacterStateBase
{public override void Enter(){Debug.Log("进入Idle状态");}public override void Update(){Debug.Log("执行Idle状态");if (Input.GetKeyUp(KeyCode.W)){stateCharacter.ChangeState(StateCharacterState.Move);}if (Input.GetMouseButtonDown(0)){stateCharacter.ChangeState(StateCharacterState.Attack);}}public override void Exit(){Debug.Log("退出Idle状态");}
}using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class StateCharacter_MoveState : StateCharacterStateBase
{float time;float timer = 3;public override void Enter(){Debug.Log("进入Move状态");}public override void Update(){Debug.Log("执行Move状态");time += Time.deltaTime;if (time > timer){time = 0;stateCharacter.ChangeState(StateCharacterState.Idle);}if (Input.GetMouseButtonDown(0)){stateCharacter.ChangeState(StateCharacterState.Attack);}}public override void Exit(){Debug.Log("退出Move状态");}
}using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class StateCharacter_AttackState : StateCharacterStateBase
{float time;float timer = 3;public override void Enter(){Debug.Log("进入Attack状态");}public override void Update(){Debug.Log("执行Attack状态");time += Time.deltaTime;if (time > timer){time = 0;stateCharacter.ChangeState(StateCharacterState.Idle);}if (Input.GetKeyUp(KeyCode.W)){stateCharacter.ChangeState(StateCharacterState.Move);}}public override void Exit(){Debug.Log("退出Attack状态");}
}

总结

第一种方法通俗易懂,但是项目庞大起来会变得极度臃肿,且改变状态限制条件有限需要手动写逻辑。

第二种方法虽然麻烦复杂,若是一个或者多个状态需要进行一个状态的切换,可能会导致多写的情况。不过解决方法简单,只需要抽象出基类即可。

推荐使用第二种,锻炼逻辑,思维清晰,不过代码量会变多。


http://www.ppmy.cn/devtools/128134.html

相关文章

LaTeX参考文献工具和宏包bibmap项目简介

LaTeX参考文献工具和宏包bibmap项目简介 LaTeX 中的参考文献生成方式主要有三种&#xff1a;第一种是手动写thebibliography环境的&#xff0c;第二种是基于bibtex程序的&#xff0c;第三种则是基于biblatex宏包和biber程序的。本文介绍的bibmap项目则提供了第四种方法。目前b…

【K8s】Kubernetes 证书管理工具 Cert-Manager

本文内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01; 如果对您有帮助&#xff0c;烦请点赞、关注、转发、订阅专栏&#xff01; 专栏订阅入口 | 精选文章 | Kubernetes | Docker | Linux | 羊毛资源 | 工具推荐 | 往期精彩文章 【Docker】&#xff08;全…

js(深浅拷贝,节流防抖,this指向,改变this指向的方法)

一、深浅拷贝 1.基本数据类型和引用数据类型的区别&#xff1a; 1. 基本数据类型的变量存储的是值 引用数据类型的变量存储的是地址值 2. 基本数据类型的变量存储的值在栈内存 引用数据类型的变量存储的值在堆内存 3. 基本数据类型的变量存储的是值和值之间相互不影响 引用数据…

Java项目-基于springboot框架的校园疫情防控系统系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…

redis过期提醒

文章目录 redis过期提醒 redis过期提醒 有一次看redis的配置文件发现一个notify-keyspace-events配置&#xff0c;注释里边长篇大论的&#xff0c;那我得看看这是干啥的&#xff0c;看完注释内容&#xff0c;发现不得了了&#xff0c;redis竟然还有过期提醒的功能 接下来得大…

Angular signal信号详细解析

Angular Signals 是 Angular 16 引入的新功能&#xff0c;专注于响应式编程&#xff0c;它通过显式的依赖追踪来提高性能和开发体验。与之前的变更检测机制不同&#xff0c;Signals 提供了一种更简洁且高效的方式来处理数据流和组件更新。 Signals 的核心概念 Signal (信号) …

3DMAX碎片生成器插件FragmentGenerator使用方法

3DMAX碎片生成器FragmentGenerator插件&#xff0c;主要应用于科研绘图方面&#xff0c;一键从选择对象体积上生成若干不规则大小凌乱排列的三角形面。 【适用版本】 3dMax2015 – 2025&#xff08;不仅限于此范围&#xff09; 【安装方法】 FragmentGenerator插件无需安装&a…

AndroidStudio实验报告——实验一、二

目录 实验一&#xff1a; AS安装与安卓环境搭建 一、实验目标 二、实验内容 &#xff08;一&#xff09;Android Studio安装 &#xff08;二&#xff09;JDK安装与配置 &#xff08;三&#xff09;Android SDK安装与配置 三、实验结果&#xff1a;&#xff08;实…