C# x Unity面向对象补全计划 设计模式 之 实现一个简单的有限状态机

一个简单的有限状态机可以有如下内容

1.状态基类(定义基本状态的方法,如进入(Enter)、执行(Execute)和退出(Exit),同时可以在此声明需要被管理的对象)

2.具体状态类(定义具体状态,如:跳跃,行走,待机,每个具体状态类继承自状态基类)

3.管理状态类(负责管理状态的切换逻辑,确保在不同状态之间进行正确的转换)

很好,那么就可以理论与实际结合了

我就按照上述内容创建一个控制角色行走,跳跃,待机可以不同切换的状态机

1.状态基类

 public abstract void Enter();public abstract void Execute();public abstract void Exit();

但是这还不够,因为没有控制角色的具体信息,试想一下,如果我想获取角色身上的刚体组件,动画组件等等基础组件,那我应该写在哪里?

具体状态类?还是管理状态类?

如果写在这两个类之中,你可能会将相同的代码多写N遍,这违背了合成复用原则

C# & Unity 面向对象补全计划 七大原则 之 合成/聚合复用原则( CARP)难度:☆☆☆☆ 总结:在类中使用类,而不是继承类-CSDN博客

So,就写在状态基类之中吧

尤其要注意的一点就是没有继承MoNo那我该上哪获取这些组件?所以要指明一个挂载到场景对象身上的脚本,也就是管理状态类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;public abstract class State 
{//Player身上的基础组件变量protected Rigidbody2D rb;protected PlayerInputAction action;protected Animator animator;protected SpriteRenderer spriteRenderer;//基础变量[Header("控制移动的变量")]public Vector2 adValue;public float playerSpeed;[Header("控制跳跃的变量")]public float jumpSpeed;public bool isJump;//尤其要说明这一点,这个是继承mono的管理类,所以要指明对象是人物的管理类protected PlayerControl playercntrol;//可以在构造函数进行初始化protected State(PlayerControl playerControl){this.playercntrol = playerControl;Instance();}//获取基础组件的函数protected void Instance(){rb= playercntrol.GetComponent<Rigidbody2D>();animator = playercntrol.GetComponent<Animator>();action = new PlayerInputAction();spriteRenderer = playercntrol.GetComponent<SpriteRenderer>();     }public abstract void Enter();public abstract void Execute();public abstract void Exit();
}

2.具体状态类

具体状态就直接继承状态基类写逻辑好了,虽然是核心功能

但是在状态机中,是最简单的,最清晰明了的部分

PS:我拿走路和跳跃举例其实不是太恰当,因为二者之间的切换条件过于简单,想象一下,如果是Boss从100血降低到50以后触发二阶段从而有一套全新的动作的话,利用状态机是不是会很清晰

行走状态

public class WalkStae : State {public WalkStae(PlayerControl playerControl) : base(playerControl) {}public override void Enter() {playerSpeed = 200;action.Enable();}public override void Execute() {//执行行走逻辑adValue = action.Player.Move.ReadValue<Vector2>();rb.velocity = new Vector2(adValue.x * Time.deltaTime * playerSpeed * 1.6f, rb.velocity.y);//设置动画animator.SetFloat("walk", Mathf.Abs(rb.velocity.x));//翻转逻辑if (adValue.x > 0) {spriteRenderer.flipX = false;}if (adValue.x < 0) {spriteRenderer.flipX = true;}     }public override void Exit() {action.Disable();   }
}

跳跃状态

注意我没写地面检测,所以用协程函数模拟了一下跳跃切换的过程 (1s)

public class JumpState : State {public JumpState(PlayerControl playerControl) : base(playerControl) {}public override void Enter() {jumpSpeed = 200;action.Enable();}public override void Execute() {//订阅跳跃事件action.Player.Jump.started += OnJumpStarted;  }public void OnJumpStarted(InputAction.CallbackContext context) {isJump = true;animator.SetBool("Jump", isJump);rb.AddForce(playercntrol.transform.up * jumpSpeed, ForceMode2D.Impulse);Debug.Log(rb.velocity);playercntrol.StartCoroutine(WaitJumpOver(isJump));}public IEnumerator WaitJumpOver(bool isJump){yield return new WaitForSeconds(1.0f);isJump =false;    }public override void Exit() {action.Player.Jump.started -= OnJumpStarted;action.Disable();}
}

3.管理状态类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem.LowLevel;public class PlayerControl : MonoBehaviour
{//设置一个当前状态用于执行与存储private State currentState;public void Start() {ChangeState(new WalkStae(this));}private void FixedUpdate() {currentState.Execute();}//切换状态的逻辑public void ChangeState(State state){//如果当前状态部不为空则退出,不然会报错if (currentState != null) {currentState.Exit();}//记录下一个状态并且开启下一个状态currentState = state;currentState.Enter();}
}

最后一个问题,也是最重要的问题,我该怎么切换不同的状态?不然写那么多状态不能切换由什么用?

        比如上面行走切换跳跃,你可以写在ChangeState之中,但是作为触发条件,这个栗子中,我建议耦合在行走类的代码中,因为这样写非常简单

private void FixedUpdate() {currentState.Execute();// 示例:按下空格键时切换到另一个状态if (Input.GetKeyDown(KeyCode.Space)) {ChangeState(new IdleState(this));}
}

        但是,如果你有n中不会频繁切换的状态,那我我建议你写在ChangeState里,就像开关一样,那么就理所当然地使用枚举和switch表达式

C# & Unity 面向对象补全计划 之 Switch 表达式(c# 8.0++)-CSDN博客

        这时再写一个枚举切换的逻辑,就完美地写出一个状态机了,我就不过多赘述了,自己试试吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/407800.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【精选】基于python的影片数据爬取与数据分析

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

软件设计师教程(第5版)第5章 软件工程基础知识(更新中)

5.1 软件工程概述 【软件工程】是指应用计算机科学、数学及管理科学等原理,以工程化的原则和方法来解决软件问题的工程&#xff0c;其目的是提高软件生产率、提高软件质量、降低软件成本。P239 5.1.1 计算机软件 计算机软件是指计算机系统中的【程序】及其【文档】。P240 【…

一文解决---IDEA汉化问题(含中英文切换)

一、英文->中文&#xff1a; ①.下载汉化包插件&#xff1a; 操作顺序&#xff1a;File->Settings->Plugins 在搜索框输入Chinese&#xff0c;然后找到 Chinese (Simplified) Language &#xff08;汉化插件&#xff09;&#xff0c;等待下载完→Install (安装)&…

OpenCV几何图像变换(9)仿射变换函数warpAffine()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 函数是应用一个仿射变换到图像上。 warpAffine 函数使用指定的矩阵对源图像进行仿射变换&#xff1a; dst ( x , y ) src ( M 11 x M 12 y M…

《机器学习》 决策树剪枝、树模型参数及案例演示

目录 一、决策树剪枝 1、什么是决策树剪枝&#xff1f; 2、如何剪枝 3、剪枝剪哪个位置的叶子结点 二、树模型参数及用法 1、参数种类 2、参数解释 1&#xff09;criterion&#xff1a;gini or entropy 2&#xff09;splitter&#xff1a;best or random 3&#xff0…

从心理学的角度,探究一下人类为什么爱玩游戏。(缓解压力、社交需求、 获得成就感)

文章目录 引言I 游戏中的美学和文化元素,是影响玩家心理状态的关键因素。音乐美工文化背景II 成年人对游戏的心理需求获得成就感社交需求缓解压力III 心流理论(Flow Theory)解释玩家虽受虐,但也其乐无穷的现象知识扩展: 心流知识扩展: 心流活动知识扩展:得性乐观(Learne…

新版本 | GreatSQL 8.0.32-26全新发布 增强“四高”诸多新特性

近日&#xff0c;GreatSQL开源数据库社区正式发布 GreatSQL 8.0.32-26新版本&#xff0c;在高可用、高性能、高兼容、高安全等诸多方面进行了特性增强&#xff0c;修复多个缺陷&#xff0c;并详细说明了多个典型应用场景下&#xff0c;升级/降级到GreatSQL 8.0.32-26的操作策略…

Linux自旋锁和读写锁

在前面的文章中我们已经介绍了有关互斥锁的概念与使用&#xff0c;本篇将开始介绍在 Linux 中的自旋锁和读写锁。这三种锁分别用于在不同的应用场景之中&#xff0c;其中互斥锁最为常用&#xff0c;但是我们需要了解一下其他的锁。 对于自旋锁和读写锁都介绍了其原理以及接口使…

游戏如何对抗 IL2cppDumper逆向分析

众所周知&#xff0c;Unity引擎中有两种脚本编译器&#xff0c;分别是 Mono 和 IL2CPP 。相较于Mono&#xff0c;IL2CPP 具备执行效率高、跨平台支持等优势&#xff0c;已被大多数游戏采用。 IL2CPP 模式下&#xff0c;可以将游戏 C# 代码转换为 C 代码&#xff0c;然后编译为…

GPT-4o System Card is released

GPT-4o System Card is released, including red teaming, frontier risk evaluations, and other key practices for industrial-strength Large Language Models. https://openai.com/index/gpt-4o-system-card/ 报告链接 企业级生成式人工智能LLM大模型技术、算法及案例实战…

UE5用蓝图实现物体A始终朝向物体B |Find Look at Rotation|

非常常用的蓝图节点 |Find Look at Rotation|&#xff1a;获取 物体A 到 物体B 的Rotator。 Tick中将算出的Rotator设置给物体A&#xff0c;即可实现永远朝向物体B

C++STL之map的使用详解

简介&#xff1a;map底层实现为红黑树&#xff0c;增删查的时间复杂度&#xff1a;O(logn), key是有序的&#xff0c;默认升序 一、初始化 #include<iostream> #include<map> #include<string> using namespace std; int main() {std::map<int, std::st…

楼顶气膜羽毛球馆:城市健身新空间—轻空间

随着城市化进程的加快&#xff0c;城市土地资源愈发紧张&#xff0c;如何高效利用有限的空间成为一大挑战。楼顶气膜羽毛球馆作为一种创新的体育场馆建设方式&#xff0c;凭借其独特的优势&#xff0c;逐渐成为城市健身的新宠。它不仅有效利用了楼顶闲置空间&#xff0c;还为市…

鸿蒙Harmony编程开发:服务端证书锁定防范中间人攻击示例

1. TLS通讯中间人攻击及防范简介 TLS安全通讯的基础是基于对操作系统或者浏览器根证书的信任&#xff0c;如果CA证书签发机构被入侵&#xff0c;或者设备内置证书被篡改&#xff0c;都会导致TLS握手环节面临中间人攻击的风险。其实&#xff0c;这种风险被善意利用的情况还是很…

【25届秋招】饿了么0817算法岗笔试

目录 1. 第一题2. 第二题3. 第三题 ⏰ 时间&#xff1a;2024/08/17 &#x1f504; 输入输出&#xff1a;ACM格式 ⏳ 时长&#xff1a;100min 本试卷还有单选和多选部分&#xff0c;但这部分比较简单就不再展示。 最近终于有时间继续整理之前的笔试题了&#xff0c;因为时间仓促…

数学建模之数据分析【九】:数据清理概述

文章目录 一、什么是数据清理二、为什么数据清理很重要三、执行数据清洁的步骤四、如何执行数据清理五、数据清理的Python库实现5.1 数据检查与探索5.2 使用df.info()检查数据信息5.3 检查分类和数字列5.4 检查分类列中唯一值的总数5.5 执行数据清理的步骤5.5.1 删除所有上述不…

C++ 设计模式——观察者模式

观察者模式 观察者模式主要组成部分例一&#xff1a;工作流程第一步&#xff1a;定义观察者接口第二步&#xff1a;定义主题接口第三步&#xff1a;实现具体主题第四步&#xff1a;实现具体观察者第五步&#xff1a;主函数UML 图UML 图解析 例二&#xff1a;工作流程第一步&…

动态规划之买卖股票篇-代码随想录算法训练营第三十八天| 买卖股票的最佳时机ⅠⅡⅢⅣ,309.最佳买卖股票时机含冷冻期,714.买卖股票的最佳时机含手续费

121. 买卖股票的最佳时机 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 讲解视频&#xff1a; 动态规划之 LeetCode&#xff1a;121.买卖股票的最佳时机1 题目描述&#xff1a; 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定…

[数据集][目标检测]电力场景输电线异物检测数据集VOC+YOLO格式2060张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2060 标注数量(xml文件个数)&#xff1a;2060 标注数量(txt文件个数)&#xff1a;2060 标注…

K8s节点状态 NotReady排查

k8s节点由 Ready变成 NotReady izbp12ghzy6koox6fqt0suz NotReady slave 97d v1.23.3 izbp12ghzy6koox6fqt0svz Ready control-plane,master 98d v1.23.3节点进入 NotReady 状态可能是由于多种原因引起的&#xff0c;尤其是在资源过量分配&am…