C# x Unity 从玩家控制类去分析命令模式该如何使用

        本文部分内容出自游戏编程模式一书,游戏编程模式,有兴趣的小伙伴可以去看看,虽然不是unity x c#写的 但是思路挺好的

目录

目录

0.先说结论

发现问题

命令模式如何解耦

打个断点更利于分析

怎么实现延迟命令?

如何撤销命令?

脚本整体一览

不足分析(AI)


8e821a2e270840b585418281a366de48.png

0.先说结论

命令模式的好处:

解耦            扩展          延迟        撤销

命令模式是将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化; 对请求排队或记录请求日志,以及支持可撤销的操作

说白了,所谓命令模式就是将操作脚本分为一大堆单个命令 然后用一个类统一管理,所以 命令是具现化的方法调用

而其一般包含如下几个部分也可以有其他的部分,我只取其中一种

  • 命令接口:定义命令的执行方法
  • 具体命令类:实现命令接口,封装具体操作
  • 命令管理类:  管理所有的具体命令类
  • 实现命令类:实现上述所有的行为

发现问题

首先 不使用命令模式来实现角色的几个行为代码如下,这是非常非常常用的方法

public class CommandPart : MonoBehaviour
{private void Update() {if(Input.GetKeyDown(KeyCode.Q)){jump();}if (Input.GetKeyDown(KeyCode.W)) {move();}if (Input.GetKeyDown(KeyCode.E)) {hit();}}public void jump() {Debug.Log("跳跃");}public void move() {Debug.Log("移动");}public void hit() {Debug.Log("击打");}
}

但是,似乎有几个问题

1.当我角色的行为一旦多起来,岂不是要在update写一大堆东西?

2.输入检测和行为方法强耦合,改键容易影响到其他代码

3.试想一下如果我有10种动作 ,CommandPart 个类说不定会扩展到一万多行,当我需要修改其中一个动作的时候

就需要在有一万多行代码的类中找一个函数中的几行关键代码,感觉很不好

命令模式如何解耦

为了演示方便 我都将其写到一个脚本中了,通常来说是写到很多个不同脚本之中的,请注意到这一点!!!

我需要一个命令基类

// 命令基类 提供一个可重写的执行方法
public abstract class Command {public abstract void Execute();
}

我给将三个行为分成三个具体命令类,并继承基类,对其方法进行封装

// 角色行为的几个方法 需要去继承
public class JumpCommand : Command {public override void Execute() {Jump();}private void Jump() {Debug.Log("跳跃");}
}public class MoveCommand : Command {public override void Execute() {Move();}private void Move() {Debug.Log("移动");}
}public class HitCommand : Command {public override void Execute() {Hit();}private void Hit() {Debug.Log("击打");}
}

之后 写一个管理脚本,传入需要执行的参数

// 命令管理器类
public class CommandCtrl{private Command command;public void ExecuteCommand(Command cmd) {command = cmd;cmd.Execute();
}   
}

最后因为里氏替换原则,通过命令管理类去实现命令

// 玩家的 实现命令类
public class CommandPart : MonoBehaviour {private CommandCtrl cCtrl;private void Awake() {cCtrl =new CommandCtrl();}private void Update() {if (Input.GetKeyDown(KeyCode.Q)) {cCtrl.ExecuteCommand(new JumpCommand());}if (Input.GetKeyDown(KeyCode.W)) {}if (Input.GetKeyDown(KeyCode.E)) {}}
}

打个断点更利于分析

按下Q之后

0f0b58de208e4dac941dcff663eb4395.png 可以清楚的看到JumpCommand的实例被传进来 执行了重写后的Execute方法

7c8bc9d8568b4c9facb0dd02aa372f2b.png

当我添加了HitCommand之后,按下对应按键

b9b5847f4f6e42b794deb951f84a9c6d.png

扩展的话就只需要多写几个具体命令类并继承命令类就行了 

怎么实现延迟命令?

修改一下命令管理类 将命令存入一个List表里面就行了,别忘了清空命令表

如果还有其他需求 可以用栈或队列去存储

en造数据结构与算法C# 用数组实现个栈还不简单???看我一秒破之!!!(unity演示)-CSDN博客

en造数据结构与算法 c#语言 数组实现队列很难???看我一击破之!!!-CSDN博客

// 命令管理器类
public class CommandCtrl{private List<Command> cmdList = new List<Command>();//执行命令public void ExecuteCommand(Command cmd) {this.cmdList.Add(cmd);cmd.Execute();}//延迟执行的函数public void DelayCommand(Command cmd){this.cmdList.Add(cmd);//满足一定条件后 执行逻辑 单纯延时用定时器 协程 Time类等       cmd.Execute();}//清空命令表public void ClearComman(){cmdList.Clear();}
}
if (Input.GetKeyDown(KeyCode.Q)) {cCtrl.DelayCommand(new JumpCommand());}

如何撤销命令?

实现思路如图

bc8b9a9bfcc0405abf3cbd857fbcaf10.png

代码我就抛砖引玉了 

  //移除最后一个命令public void UndoCommand(){       if(cmdList.Count>0){cmdList.RemoveAt(cmdList.Count - 1);//这里执行游戏回溯 比如位置回溯 金币回溯等等}}

脚本整体一览

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;// 命令基类 提供一个可重写的执行方法
public abstract class Command {public abstract void Execute();
}// 角色行为的几个方法 需要去继承
public class JumpCommand : Command {public override void Execute() {Jump();}private void Jump() {Debug.Log("跳跃");}
}public class MoveCommand : Command {public override void Execute() {Move();}private void Move() {Debug.Log("移动");}
}public class HitCommand : Command {public override void Execute() {Hit();}private void Hit() {Debug.Log("击打");}
}// 命令管理器类
public class CommandCtrl{private List<Command> cmdList = new List<Command>();//执行命令public void ExecuteCommand(Command cmd) {this.cmdList.Add(cmd);cmd.Execute();}//延迟执行的函数public void DelayCommand(Command cmd){this.cmdList.Add(cmd);//满足一定条件后 执行逻辑 单纯延时用定时器 协程 Time类等       cmd.Execute();}//移除最后一个命令public void UndoCommand(){       if(cmdList.Count>0){cmdList.RemoveAt(cmdList.Count - 1);//这里执行游戏回溯 比如位置回溯 金币回溯等等}}//清空命令表public void ClearComman(){cmdList.Clear();}
}
// 玩家的 实现命令类
public class CommandPart : MonoBehaviour {private CommandCtrl cCtrl;private void Awake() {cCtrl =new CommandCtrl();}private void Update() {if (Input.GetKeyDown(KeyCode.Q)) {cCtrl.DelayCommand(new JumpCommand());}if (Input.GetKeyDown(KeyCode.W)) {cCtrl.ExecuteCommand(new HitCommand());}if (Input.GetKeyDown(KeyCode.E)) {cCtrl.ExecuteCommand(new MoveCommand());}}
}

不足分析(AI)

命令模式的基础框架比较简单的,但是要完善起来还是需要具体问题具体分析

以下是这段代码存在的一些不足之处:

### 1. 延迟执行逻辑不完善 在 `CommandCtrl` 类的 `DelayCommand` 方法中,虽然有添加命令到列表以及执行命令的操作,但目前对于延迟执行的关键部分,只是简单提及可以用定时器、协程等方式,却没有真正合理地实现延迟逻辑。像这样直接执行命令,并没有达到真正延迟执行的效果,和普通执行命令没有实质性区别,无法满足实际需要延迟一段时间后再执行命令的场景需求

### 2. 撤销功能不完整 `CommandCtrl` 类里的 `UndoCommand` 方法目前只是从命令列表中移除了最后一个命令,但对于一些具有实际行为影响的命令(比如移动了角色位置、改变了游戏场景状态等),仅仅移除命令本身并没有回退这些因命令执行而产生的影响。例如角色执行了移动命令后,撤销操作不仅要移除该命令,还应该将角色位置恢复到移动前的状态,需要额外的逻辑来记录和还原执行命令前的相关状态信息,才能实现完整有效的撤销功能。

### 3. 代码复用性与灵活性受限 - **输入绑定方面**:在 `CommandPart` 类的 `Update` 方法中,按键与具体命令的关联是硬编码的方式(通过 `Input.GetKeyDown` 判断具体按键来执行对应的命令)。如果后续想要更改按键绑定,或者增加新的输入方式(如手柄操作等),就必须修改 `Update` 方法中的代码,代码复用性较差,不够灵活。 - **命令执行方面**:目前的代码结构对于命令的使用相对固定,如果想在其他地方复用这些命令执行相关逻辑(比如在不同的游戏角色控制或者不同的游戏模式下复用命令的执行、延迟、撤销等操作),不太方便,缺乏更灵活的配置和调用机制来适应多种场景需求

### 4. 缺乏错误处理机制 整个代码中基本没有对可能出现的异常情况进行处理。例如在执行命令过程中,如果某个具体命令类内部的逻辑出现错误(比如空引用、越界访问等情况),或者在操作命令列表(添加、移除等操作)时出现异常,程序可能会直接崩溃或者出现不符合预期的行为,影响程序的稳定性和健壮性

### 5. 命令类功能扩展性不足 以现有的具体命令类(如 `JumpCommand`、`MoveCommand`、`HitCommand` )来看,它们内部的 `Execute` 方法直接调用对应的私有方法来实现行为,这种方式在需要对命令的执行添加更多参数或者条件时扩展性不佳。比如想要实现不同力度的击打命令,或者不同高度、距离的跳跃命令等,较难在现有结构上方便地扩展功能,需要对代码结构做一定调整

### 6. 类职责不够清晰明确 `CommandCtrl` 类承担了较多功能,包括命令执行、延迟执行(虽然目前没完善好)、撤销以及清空命令列表等,随着功能进一步扩展和复杂,这个类可能会变得越来越臃肿,不利于代码的维护和阅读,各功能模块的职责没有更加细分和独立出来,不符合单一职责原则。 针对这些不足,可以通过进一步优化延迟执行逻辑、完善撤销功能的状态记录与还原、采用配置化的输入绑定方式、添加异常处理代码、改进命令类的设计以增强扩展性以及对 `CommandCtrl` 类进行职责细分等措施来提升代码的质量和功能完整性

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

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

相关文章

Day44 | 动态规划 :状态机DP 买卖股票的最佳时机IV买卖股票的最佳时机III

Day44 | 动态规划 &#xff1a;状态机DP 买卖股票的最佳时机IV&&买卖股票的最佳时机III&&309.买卖股票的最佳时机含冷冻期 动态规划应该如何学习&#xff1f;-CSDN博客 本次题解参考自灵神的做法&#xff0c;大家也多多支持灵神的题解 买卖股票的最佳时机【…

IDEA2024:右下角显示内存

使用场景&#xff1a; 实时知晓idea内存使用情况 解决方案: 开启内存显示 View -> Apperance -> Status Bar Widgets -> Memory Indicator 效果如下&#xff1a;

HBase理论_背景特点及数据单元及与Hive对比

本文结合了个人的笔记以及工作中实践经验以及参考HBase官网&#xff0c;我尽可能把自己的知识点呈现出来&#xff0c;如果有误&#xff0c;还请指正。 1. HBase背景 HBase作为面向列的数据库运行在HDFS之上&#xff0c;HDFS缺乏随机读写操作&#xff0c;HBase正是为此而出现。…

git创建远程仓库,以gitee码云为例GitHub同理

git远程Remote服务端仓库构建的视频教程在这 Git建立服务端Remote远程仓库&#xff0c;gitee码云例&#xff0c;Github_哔哩哔哩_bilibili 1、登gitee码云/Github 登录 - Gitee.com https://github.com/ &#xff08;没账号的注册一下就行&#xff09; 点击如下图位置的创…

windows工具 -- 使用rustdesk和云服务器自建远程桌面服务, 手机, PC, Mac, Linux远程桌面 (简洁明了)

目的 向日葵最先放弃了, todesk某些功能需要收费, 不想用了想要 自己搭建远程桌面 自己使用希望可以电脑 控制手机分辨率高一些 原理理解 ubuntu云服务器配置 够买好自己的云服务器, 安装 Ubuntu操作系统 点击下载 hbbr 和 hbbs 两个 deb文件: https://github.com/rustdesk/…

计算机网络各层设备总结归纳(更新ing)

计算机网络按照OSI&#xff08;开放式系统互联&#xff09;模型分为七层&#xff0c;每一层都有其特定的功能和对应的网络设备。以下是各层对应的设备&#xff1a; 1. 物理层&#xff08;Physical Layer) 设备&#xff1a;中继器&#xff08;Repeater&#xff09;、集线器…

Oracle19C AWR报告分析之Wait Classes by Total Wait Time

Oracle19C AWR报告分析之Wait Classes by Total Wait Time 一、分析数据二、详细分析2.1 指标参数介绍2.2 数据库性能分析2.3 综合性能评估 在 Oracle 数据库的 AWR 报告中&#xff0c;Wait Classes by Total Wait Time 是评估数据库性能的重要部分。本篇文章主要是介绍指标参数…

基本数据类型和包装类型的区别、缓存池、自动拆箱装箱(面试题)

目录 1. 八种基本类型及对应包装类型 2. 基本类型和包装类型 区别 3. 自动拆箱装箱 3.1 自动装箱 3.2 自动拆箱 3.3 缓存池 4. 高频面试案例分析 1. 八种基本类型及对应包装类型 基本数据类型类型描述范围&#xff08;指数形式&#xff09;位数包装类型byte整型&#x…

Python酷库之旅-第三方库Pandas(221)

目录 一、用法精讲 1036、pandas.DatetimeIndex.to_pydatetime方法 1036-1、语法 1036-2、参数 1036-3、功能 1036-4、返回值 1036-5、说明 1036-6、用法 1036-6-1、数据准备 1036-6-2、代码示例 1036-6-3、结果输出 1037、pandas.DatetimeIndex.to_series方法 10…

基于SpringBoot网上超市的设计与实现录像

基于SpringBoot网上超市的设计与实现录像 SpringBoot网上超市的设计与实现录像

【vmware+ubuntu16.04】vm虚拟机及镜像安装-tools安装包弹不出来问题

学习机器人这门课需要下载虚拟机&#xff0c;做一下记录 首先我下载的是vm虚拟机16&#xff0c; 下载版本可参考该文章课堂上我下载 的镜像是16.04&#xff0c;虚拟机安装教程和镜像添加可参考该博主 按照教程安装成功 安装tools&#xff0c;但是我的弹不出来那个压缩包&…

ssm126基于HTML5的出租车管理系统+jsp(论文+源码)_kaic

设计题目&#xff1a;出租车管理系统的设计与实现 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以…

游戏引擎学习第14天

视频参考:https://www.bilibili.com/video/BV1iNUeYEEj4/ 1. 为什么关注内存管理&#xff1f; 内存分配是潜在的失败点&#xff1a; 每次进行内存分配&#xff08;malloc、new等&#xff09;时&#xff0c;都可能失败&#xff08;例如内存不足&#xff09;。这种失败会引入不稳…

阿里云引领智算集群网络架构的新一轮变革

阿里云引领智算集群网络架构的新一轮变革 云布道师 11 月 8 日~ 10 日在江苏张家港召开的 CCF ChinaNet&#xff08;即中国网络大会&#xff09;上&#xff0c;众多院士、教授和业界技术领袖齐聚一堂&#xff0c;畅谈网络未来的发展方向&#xff0c;聚焦智算集群网络的创新变…

【更新至2023】A股上市公司企业突破性创新、渐进性创新数据(2000-2023年)

测算方式&#xff1a;参考C刊《财经问题研究》胡山&#xff08;2022&#xff09;老师的研究&#xff0c;用当年获得授权的发明专利数量加 1 后取自然对数来衡量企业突破性创新 ( Invention) ; 用非发明专利 ( 包括实用新型专利和外观设计专利) 授权量加 1 后取自然对数来衡量企…

【Android、IOS、Flutter、鸿蒙、ReactNative 】启动页

Android 设置启动页 自定义 splash.xml 通过themes.xml配置启动页背景图 IOS 设置启动页 LaunchScreen.storyboard 设置为启动页 storyboard页面绘制 Assets.xcassets 目录下导入图片 AppLogo Flutter 设置启动页 Flutter Android 设置启动页 自定义 launch_background.xm…

Elasticsearch:管理和排除 Elasticsearch 内存故障

作者&#xff1a;来自 Elastic Stef Nestor 随着 Elastic Cloud 提供可观察性、安全性和搜索等解决方案&#xff0c;我们将使用 Elastic Cloud 的用户范围从完整的运营团队扩大到包括数据工程师、安全团队和顾问。作为 Elastic 支持代表&#xff0c;我很乐意与各种各样的用户和…

Jmeter基础篇(24)Jmeter目录下有哪些文件夹是可以删除,且不影响使用的呢?

一、前言 Jmeter使我们日常做性能测试最常用的工具之一啦&#xff01;但是我们在和其他同学协同工作的时候&#xff0c;偶尔也会遇到一些问题&#xff0c;例如我想要给别人发送一个Jmeter工具包&#xff0c;但这个文件包往往会很大&#xff0c;比较浪费流量和空间&#xff0c;…

排序算法(基础)大全

一、排序算法的作用&#xff1a; 排序算法的主要作用是将一组数据按照特定的顺序进行排列&#xff0c;使得数据更加有序和有组织。 1. 查找效率&#xff1a;通过将数据进行排序&#xff0c;可以提高查找算法的效率。在有序的数据中&#xff0c;可以使用更加高效的查找算法&…

如何在 WordPress 中轻松强制所有用户退出登录

作为一名长期管理 WordPress 网站的站长&#xff0c;我深知维护网站安全性的重要性。尤其是在面对会员网站或付费内容平台时&#xff0c;确保所有用户的登录状态是最新的&#xff0c;是维持网站正常运营的关键之一。今天&#xff0c;我就分享一下如何通过简单的步骤&#xff0c…