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

news2024/12/23 3:14:42

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

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));
    }
}

        但是,如果你的是真的两种不会频繁切换的状态,那我我建议你写在ChangeState里,就像开关一样

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电动汽车是否将成为银的最大需求端?

近年来&#xff0c;工业对银的需求一直在激增。主要是由于太阳能面板制造商对银的大量需求&#xff0c;预计2023年的需求量将比前一年增长11%。预测者预计今年又会增长9%。 然而&#xff0c;对于银的投资者来说&#xff0c;未来制造商对银的需求前景甚至可能比最近的过去更加明…

使用Linux Systemd部署DotNet Quartz.Net定时任务

开发环境 Windows 10 WSL2Ubuntu 22.04DotNet 6Quartz.Net 代码实战 新建dotnet项目&#xff0c;添加引用Quartz.net包 入口程序&#xff1a; static void Main(string[] args){IConfiguration configuration new ConfigurationBuilder().SetBasePath(Directory.GetCurren…

苹果手机白屏是怎么回事?解决方法分享

苹果手机作为市场上最受欢迎的智能手机之一&#xff0c;其稳定性和流畅性一直备受用户赞誉。然而&#xff0c;偶尔我们也会遇到一些令人头疼的问题&#xff0c;比如苹果手机出现白屏无反应的情况。那么&#xff0c;苹果手机白屏到底是怎么回事呢&#xff1f;本文将为大家详细解…

[RCTF2019]draw

下载是一个文本文档&#xff0c;百度AI cs pu lt 90 fd 500 rt 90 pd fd 100 rt 90 repeat 18[fd 5 rt 10] lt 135 fd 50 lt 135 pu bk 100 pd setcolor pick [ red orange yellow green blue violet ] repeat 18[fd 5 rt 10] rt 90 fd 60 rt 90 bk 30 rt 90 fd 60 pu lt 90 f…

开放式耳机有什么好处?五款高口碑优质爆款直入!

开放式耳机提供了多种好处&#xff0c;尤其适合特定的使用场景和用户群体。以下是开放式耳机的一些显著优势&#xff1a; 1. 佩戴舒适性&#xff1a;开放式耳机不堵塞耳道&#xff0c;允许空气流通&#xff0c;减少耳朵内部的潮湿和压力&#xff0c;适合长时间佩戴&#xff0c…

Tomcat部署项目get请求中文乱码

问题描述 tomcat部署的项目&#xff0c;get请求到后端后&#xff0c;打印日志发现通过RequestParam()接收的参数值乱码。 问题猜测 编码错误导致的乱码。 流程梳理 浏览器发送请求时会自动对请求链接中自带的参数进行编码。编码时一般都是采用UTF-8的格式进行编码。请求到…

电脑无法新建 Word Excle PPT 这些文件是咋回事

咦 我的电脑怎么没有 Excel文件 Word文件 和 PPT选项嘞 &#xff01;&#xff01; 今天突然要写个材料&#xff0c;发现自己新建文件竟然没有excel文档 word和ppt幻灯片这些选项。哦 原来是我自己上次把电脑从win7升级win10系统之后还没有安装wps这些所以不能使用。如果你的电…

SIRA-PCR: Sim-to-Real Adaptation for 3D Point Cloud Registration 论文解读

目录 一、导言 二、 相关工作 1、三维点云配准工作 2、无监督域适应 三、SIRA-PCR 1、FlyingShape数据集 2、Sim-to-real自适应方法 3、配准 4、损失函数 一、导言 该论文来自于ICCV2023&#xff0c;论文提出了一种新的方法SIRA-PCR&#xff0c;通过利用合成数据Flying…

[RoarCTF2019]黄金6年

下载解压得一个MP4视频文件 播放了一下几秒过&#xff0c;太快了&#xff0c;使用Potplayer做视频切片&#xff0c;空格暂停播放&#xff0c;使用D F两个快捷键逐帧查看&#xff08;F前进一帧&#xff0c;D后退一帧&#xff09; 发现一个二维码&#xff0c;扫码得key1&#xf…

PiX4Dmatic1.63 Pix4Dsurvey1.63 实景三维建模软件功能介绍 下载软件License使用

PIX4D matic摄影测量软件是一款非常不错的摄影测量软件&#xff0c;这款软件用于廊道和大比例尺测绘的下一代摄影测量软件&#xff0c;PIX4D matic也支持常用的垂直坐标系及其相应的大地水准面。 PIX4D matic(摄影测量软件)是一款非常不错的摄影测量软件&#xff0c;这款软件用…

如果提议者提议区块的时间晚了,会对见证有何影响?

原文标题&#xff1a;《On Attestations, Block Propagation, and Timing Games》 撰文&#xff1a;Nero_eth 编译&#xff1a;Tia&#xff0c;Techub News 如今&#xff0c;提议者的时序博弈已经很常见了&#xff0c;很多研究也都在分析这一现象。 本篇文章将带大家了解提议…

windows上传的文本在linux执行不了,格式转换

在windows编辑的文件脚本上传到linux里面执行不了 1.现象描述 比如在windows编辑简单的文本 2.上传到linux后执行无结果 无响应 3.编码问题 比普通文件多了with CRLF line terminators结尾格式。 cat -v 可以让隐藏的转义字符也打印中显示 4.原因windows和linux的换行符不…

逐级删除空目录 如果目录非空则停止删除操作 os.removedirs(path)

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 逐级删除空目录 如果目录非空 则停止删除操作 os.removedirs(path) [太阳]选择题 下列关于代码和os.removedirs(path)函数说法正确的是&#xff1f; import os os.makedirs("D:/test1…

GAMES101——作业5 光线与三角形相交(菲涅尔反射率)

任务 需要修改的函数是&#xff1a; Renderer.cpp 中的 Render() &#xff1a;这里你需要为每个像素生成一条对应的光线&#xff0c;然后调用函数 castRay() 来得到颜色&#xff0c;最后将颜色存储在帧缓冲区的相应像素中。 Triangle.hpp 中的 rayTriangleIntersect() :…

测试八股文(自总版)(更新中...)

测试八股文---从网上各处搜罗滴 软件测试的一些基础知识1. 什么是软件&#xff1f;1. 什么是软件测试?2. 软件测试分类?3. 软件生命周期的各阶段4. 几个模型---瀑布模型 , V模型 , 敏捷开发模型5. 软件测试基本流程&#xff08;1&#xff09;需求分析测试需求分析具体怎么来进…

力扣爆刷第174天之TOP200五连刷136=140(最小k数、字典序、跳跃游戏)

力扣爆刷第174天之TOP200五连刷136140&#xff08;最小k数、字典序、跳跃游戏&#xff09; 文章目录 力扣爆刷第174天之TOP200五连刷136140&#xff08;最小k数、字典序、跳跃游戏&#xff09;一、LCR 159. 库存管理 III二、450. 删除二叉搜索树中的节点三、440. 字典序的第K小…

【C语言】进程和线程详解

目录 C语言进程和线程详解1. 进程和线程的对比2. 进程的基本概念2.1 进程的定义2.2 进程的特点2.3 进程的生命周期 3. 进程管理3.1 进程创建3.2 进程间通信&#xff08;IPC&#xff09;3.2.1 管道&#xff08;Pipe&#xff09; 4. 线程的基本概念4.1 线程的定义4.2 线程的特点 …

Halcon灰度图像的形态学运算

Halcon灰度图像的形态学运算 本文介绍的算子的输入类型是灰度的Image图像。 1. 灰度图像与区域的区别 基于区域的形态学运算与基于灰度图像的形态学运算的根本区别在于&#xff0c;二者输入的对象不同。前者输入的是一些区域&#xff0c;并且这些区域是经过闽值处理的二值图…

微信小程序在线客服源码系统全端通吃 带完整的安装代码包以及搭建部署教程

系统概述 “微信小程序在线客服源码系统全端通吃”是一款集智能客服、人工客服、消息管理、数据分析等功能于一体的综合性解决方案。该系统基于微信小程序平台开发&#xff0c;支持全端接入&#xff08;包括Web、App、小程序等&#xff09;&#xff0c;实现多渠道客户服务的无…

英国海外媒体通稿宣发:顶级媒体宣发

1.伦敦日报londonjournal 作为英国首都的权威日报&#xff0c;伦敦日报一直是英国新闻界的佼佼者。它详尽报道伦敦及英国各地的政治、经济、社会、文化、体育等各方面的新闻&#xff0c;深受读者喜爱。 2.英国先驱报ukherald 英国先驱报是一份全国性日报&#xff0c;以深度分…