当前位置: 首页 > news >正文

《游戏编程模式》学习笔记(七)状态模式 State Pattern

状态模式的定义

允许对象在当内部状态改变时改变其行为,就好像此对象改变了自己的类一样。

举个例子

在书的示例里要求你写一个人物控制器,实现跳跃功能
直觉上来说,我们代码会这么写:

void Heroine::handleInput(Input input)
{if (input == PRESS_B){yVelocity_ = JUMP_VELOCITY;setGraphics(IMAGE_JUMP);}
}
可是这么写不对,因为人物本身应该只能跳一次,这样写的话人物就可以无限按B实现跳跃了。我们加一个bool变量来限制跳跃的情况。
void Heroine::handleInput(Input input)
{if (input == PRESS_B){if (!isJumping_){isJumping_ = true;// 跳跃……}}
}

好的,现在还要加一个趴下的功能,松开按键还得能站起来。如果我们这么加代码:

void Heroine::handleInput(Input input)
{if (input == PRESS_B){// 如果没在跳跃,就跳起来……}else if (input == PRESS_DOWN){if (!isJumping_){setGraphics(IMAGE_DUCK);}}else if (input == RELEASE_DOWN){setGraphics(IMAGE_STAND);}
}

实际上就会出bug,如果玩家在趴下的状态下按了B跳起,此时再松开趴下键,人物就会在空中变成站立的姿势。那么为了防止这种情况的发生,我们又加了一个bool变量来标识趴下的情况:

void Heroine::handleInput(Input input)
{if (input == PRESS_B){if (!isJumping_ && !isDucking_){// 跳跃……}}else if (input == PRESS_DOWN){if (!isJumping_){isDucking_ = true;setGraphics(IMAGE_DUCK);}}else if (input == RELEASE_DOWN){if (isDucking_){isDucking_ = false;setGraphics(IMAGE_STAND);}}
}

这段代码已经很臃肿了,如果我们还想让人物实现移动,是不是又得加个标志位?再进一步,人物如果要实现攻击呢?代码就会越来越复杂……
这个时候我们就需要FSM来救场了。
(这里说的FSM和状态模式是同一个东西,下同)
FSM的要点:
在这里插入图片描述

顺着这个思路,这里列出一个最简单的FSM,我们先用枚举定义状态:

enum State
{STATE_STANDING,STATE_JUMPING,STATE_DUCKING,STATE_DIVING
};

在之前的代码中,我们先判断输入,再根据状态的不同做判断。但是在这里,我们让处理状态的代码聚在一起,所以先对状态做分支。这样的话:

void Heroine::handleInput(Input input)
{switch (state_){case STATE_STANDING:if (input == PRESS_B){state_ = STATE_JUMPING;yVelocity_ = JUMP_VELOCITY;setGraphics(IMAGE_JUMP);}else if (input == PRESS_DOWN){state_ = STATE_DUCKING;setGraphics(IMAGE_DUCK);}break;case STATE_JUMPING:if (input == PRESS_DOWN){state_ = STATE_DIVING;setGraphics(IMAGE_DIVE);}break;case STATE_DUCKING:if (input == RELEASE_DOWN){state_ = STATE_STANDING;setGraphics(IMAGE_STAND);}break;}
}

我们扔掉了烦人的标志位,简化了状态的变化,将其变成了字段,然后将处理所有状态的代码都聚集在了一起。这就是最简单的一种FSM。
现在让我们更进一步,看看对于复杂情况,我们要如何构建一个状态模式控制下的人物逻辑。
对于一些复杂的状态,我们有时候既要处理输入,又要处理时间。因为有些状态会根据按下时间的长短进行改变。
比如,现在趴下一定时间后会进行充能,充能后发动的攻击威力更大。
我们以此为目标,按照面向对象的逻辑,我们先写一个状态基类:

class HeroineState
{
public:virtual ~HeroineState() {}virtual void handleInput(Heroine& heroine, Input input) {}virtual void update(Heroine& heroine) {}
};

这里的handleInput()就是处理输入的接口,update()就是处理状态随着时间变化的接口。
我们再以此为基础,写趴下状态,将其单独变为一个类,并且继承这个基类:

class DuckingState : public HeroineState
{
public:DuckingState(): chargeTime_(0){}virtual void handleInput(Heroine& heroine, Input input) {if (input == RELEASE_DOWN){// 改回站立状态……heroine.setGraphics(IMAGE_STAND);}}virtual void update(Heroine& heroine) {chargeTime_++;if (chargeTime_ > MAX_CHARGE){heroine.superBomb();}}private:int chargeTime_;
};

这样,我们在人物Heroine的类中添加当前状态的指针,就可以让人物拥有趴下的状态了:

class Heroine
{
public:virtual void handleInput(Input input){state_->handleInput(*this, input);}virtual void update(){state_->update(*this);}private:HeroineState* state_;
};

要改变状态,只要让指针指向别的地方就OK了。
这就是一个面向对象式的,相对复杂的状态模式的实现方式。是不是还算很简单?

一些细节

如果状态中不存储数据,或者只有全程只有一个人物拥有这些状态,你可以直接静态声明这些状态,将其放在全局存储区内。但如果这些状态包含着数据,就像上边的例子中的chargeTime,你就需要考虑把这些状态实例化,以便管理。
有时候你需要对状态加入入口行为和出口行为来控制状态的转换。例如在每个状态的入口行为方法中改变人物的贴图等等。

原文: https://gpp.tkchu.me/state.html

相关文章:

《游戏编程模式》学习笔记(七)状态模式 State Pattern

状态模式的定义 允许对象在当内部状态改变时改变其行为,就好像此对象改变了自己的类一样。 举个例子 在书的示例里要求你写一个人物控制器,实现跳跃功能 直觉上来说,我们代码会这么写: void Heroine::handleInput(Input input…...

博客系统之功能测试

博客系统共有:用户登录功能、发布博客功能、查看文章详情功能、查看文章列表功能、删除文章功能、退出功能 1.登录功能: 1.1测试对象:用户登录 1.2测试用例 方法:判定表 用例 编号 操作步骤预期结果实际结果截图1 1.用户名正确…...

CJS和 ES6 的语法区别

CommonJS 使用 module.exports 导出模块。ES6 使用 export 导出模块。 示例代码: CommonJS(CJS)模块的导出: // 导出模块 module.exports {foo: bar,baz: function() {return qux;} }; ES6 模块的导出: // 导出模…...

ArcGIS Pro如何制作不规则形状图例

在默认的情况下,ArcGIS Pro生成的图例是标准的点、直线和矩形的,对于湖泊等要素而言,这样的表示方式不够直观,我们可以将其优化一下,制作不规则的线和面来代替原有图例,这里为大家介绍一下制作方法&#xf…...

微软Win11 Dev预览版Build23526发布

近日,微软Win11 Dev预览版Build23526发布,修复了不少问题。牛比如斯Microsoft,也有这么多bug,所以你写再多bug也不作为奇啊。 主要更新问题 [开始菜单] 修复了在高对比度主题下,打开开始菜单中的“所有应…...

【NEW】视频云存储EasyCVR平台H.265转码配置增加分辨率设置

关于视频分析EasyCVR视频汇聚平台的转码功能,我们在此前的文章中也介绍过不少,感兴趣的用户可以翻阅往期的文章进行了解。 安防视频集中存储EasyCVR视频监控综合管理平台可以根据不同的场景需求,让平台在内网、专网、VPN、广域网、互联网等各…...

【数据结构】如何用队列实现栈?图文详解(LeetCode)

LeetCode链接:225. 用队列实现栈 - 力扣(LeetCode) 本文默认读者已经掌握栈与队列的基本知识 或者先看我的另一篇博客:【数据结构】栈与队列_字节连结的博客-CSDN博客 做题思路 由于我们使用的是C语言,不能直接使用队…...

Linux 虚拟机Ubuntu22.04版本通过远程连接连接不上,输入ifconfig只能看到127.0.0.1的解决办法

之前给虚拟机配置静态IP之后,可以直接通过主机Vscode远程连接。但是前一段时间把主机的TCP/IPV4静态IP设置了一下之后,再连接虚拟机就连不上了,于是参考解决虚拟机不能上网ifconfig只显示127.0.0.1的问题,又可以连接上了&#xff…...

C语言刷题训练DAY.9

1.线段图案 解题思路&#xff1a; 这里非常简单&#xff0c;我们只需要用一个循环控制打印即可。 解题代码&#xff1a; #include<stdio.h> int main() {int n 0;while ((scanf("%d", &n)) ! EOF){int i 0;for (i 0; i < n; i){printf("*&…...

CTFHub php://input

1.首先看代码&#xff1a; 这里其实就应该想到的是php://伪协议&#xff1a; php://filter、php://input、php://filter用于读取源码 php://input用于执行php代码 2.其次&#xff0c;判断使用php://input伪协议 而执行php://input伪协议条件是allow_url_include是On 可以先利用…...

React Native expo项目修改应用程序名称

https://expo.dev/accounts/xutongbao/projects npm install --global eas-cli && \eas init --id e32cf2c0-da5b-4a65-814a-4958d58f0ca7 eas init --id e32cf2c0-da5b-4a65-814a-4958d58f0ca7 app.config.js: export default {name: 学习,slug: learn-gpt,owner: x…...

unity 之Transform组件(汇总)

文章目录 理论指导结合例子 理论指导 当在Unity中处理3D场景中的游戏对象时&#xff0c;Transform 组件是至关重要的组件之一。它管理了游戏对象的位置、旋转和缩放&#xff0c;并提供了许多方法来操纵和操作这些属性。以下是关于Transform 组件的详细介绍&#xff1a; 位置&a…...

基于Opencv的虚拟拖拽项目

预备知识 勾股定理 跟随移动算法 手势识别图解 项目源代码 """ 演示一个简单的虚拟拖拽 步骤&#xff1a; 1、opencv 读取视频流 2、在视频图像上画一个方块 3、通过mediapipe库获取手指关节坐标 4、判断手指是否在方块上 5、是&#xff0c;方块跟着移动 6、…...

基于单片机DHT11温湿度NRF2401无线通信控制系统

一、系统方案 本设计采用STC89C5单片机作为主控制器&#xff0c;从机采用DHT11传感器采集温湿度、按键设置报警阀值&#xff0c;液晶1602显示&#xff0c;蜂鸣器报警&#xff0c;无线NRF2401模块。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统…...

AutoSAR配置与实践(基础篇)2.5 RTE对数据一致性的管理

传送门 点击返回 ->AUTOSAR配置与实践总目录 AutoSAR配置与实践&#xff08;基础篇&#xff09;2.5 RTE对数据一致性的管理 一、 数据一致性问题引入二、 数据一致性的管理2.1 RTE管理 (SWC间)2.2 中断保护 (SWC内)2.3 变量保护IRVS (SWC内)2.4 Task分配2.5 任务抢占控制 一…...

ASP.NET WEB API通过SugarSql连接MySQL数据库

注意&#xff1a;VS2022企业版可以&#xff0c;社区版可能存在问题。实体名称和字段和数据库中的要一致。 1、创建项目&#xff0c;安装SqlSugarCore、Pomelo.EntityFrameworkCore.MySql插件 2、文件结构 2、appsettings.json { “Logging”: { “LogLevel”: { “Default”: …...

08-微信小程序视图层

08-微信小程序视图层 文章目录 视图层 ViewWXML数据绑定列表渲染条件渲染模板引用importimport 的作用域include WXSS尺寸单位样式导入内联样式选择器全局样式与局部样式 WXS注意事项页面渲染数据处理 视图层 View 框架的视图层由 WXML 与 WXSS 编写&#xff0c;由组件来进行…...

[机器学习]特征工程:特征降维

特征降维 1、简介 特征降维是指通过减少特征空间中的维度&#xff0c;将高维数据映射到一个低维子空间的过程。 在机器学习和数据分析中&#xff0c;特征降维可以帮助减少数据的复杂性、降低计算成本、提高模型性能和可解释性&#xff0c;以及解决维度灾难等问题。特征降维通…...

12. Docker可视化工具

目录 1、前言 2、Docker UI 2.1、部署Docker UI 2.2、管理容器 3、Portainer 3.1、部署Portainer 3.2、管理容器 3.3、添加远程Docker 4、Shipyard 1、前言 Docker 提供了命令行工具来管理 Docker 的镜像和运行 Docker 的容器。我们也可以使用图形工具来管理 Docker。…...

css层叠关系

文章目录 cascading声明冲突应用重置样式表a元素的类选择器顺序问题 cascading cascading – 层叠 解决声明冲突的过程&#xff0c;浏览器会自动处理&#xff1b;就是计算样式的权重&#xff0c;权重大的就被选择&#xff1b; 声明冲突 是指多个选择器选中同一个标签&#x…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

测试markdown--肇兴

day1&#xff1a; 1、去程&#xff1a;7:04 --11:32高铁 高铁右转上售票大厅2楼&#xff0c;穿过候车厅下一楼&#xff0c;上大巴车 &#xffe5;10/人 **2、到达&#xff1a;**12点多到达寨子&#xff0c;买门票&#xff0c;美团/抖音&#xff1a;&#xffe5;78人 3、中饭&a…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

PAN/FPN

import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...