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

news2024/12/21 17:00:22

        本文部分内容出自游戏编程模式一书,游戏编程模式,有兴趣的小伙伴可以去看看,虽然不是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.coloradmin.cn/o/2242100.html

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

相关文章

【C++】—— stack和queue的模拟实现

前言 ​ stack 和 queue使用起来都非常简单&#xff0c;现在来模拟实现一下&#xff0c;理解其底层的原理。 ​ 在实现之前&#xff0c;应该知道&#xff0c;stack 和 queue 都是容器适配器&#xff0c;通过看官网文件也可以看出来&#xff1b;其默认的容器都是deque&#xff…

探索高效的 Prompt 框架:RBTR 提示框架的奥秘与优势

前言 在当今数字化的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为我们生活和工作中不可或缺的一部分。而 Prompt 作为与 AI 交互的关键工具&#xff0c;其质量直接影响着我们获取信息的准确性和有用性。今天&#xff0c;我们将深入探讨一个通用的 Prompt 框架…

动态规划-完全背包问题——322.零钱兑换

1.题目解析 题目来源 322.零钱兑换——力扣 测试用例 2.算法原理 1.状态表示 这里需要寻找硬币使总面值等于一个值求出所需硬币的最小个数&#xff0c;所以不妨设置一个二维dp表&#xff0c;即dp[i][j]&#xff1a;在[1,i]个硬币中选择的硬币总面值完全等于j时所需要的最小硬…

day-82 最少翻转次数使二进制矩阵回文 I

思路 依次计算使行回文和列回文的的翻转次数&#xff0c;返回较小的次数即可 解题过程 计算行翻转次数&#xff1a;对于每一行&#xff0c;如果第一个数与最后一个数不相同&#xff08;前提前一个数的索引小于后一个数的索引&#xff09;则需翻转一次&#xff0c;将所有行翻转…

计算光纤色散带来的相位移动 matlab

需要注意的地方 1.以下内容纯属个人理解&#xff0c;很有可能不准确&#xff0c;请大家仅做参考 2.光速不要直接用3e8 m/s&#xff0c;需要用精确的2.9979.... 3.光的频率无论在真空还是光纤(介质)都是不变的&#xff0c;是固有属性&#xff0c;但是波长lambdac/f在不同的介…

15分钟学 Go 第 53 天 :社区资源与学习材料

第53天&#xff1a;社区资源与学习材料 目标 了解Go语言官方资源掌握社区重要学习平台学会利用开源项目学习构建个人知识体系 一、Go语言官方资源汇总 资源类型网址说明Go官网golang.org官方文档、下载、教程Go Blogblog.golang.org技术博客、最新特性介绍Go Playgroundpla…

丹摩征文活动 |【前端开发】HTML+CSS+JavaScript前端三剑客的基础知识体系了解

前言 &#x1f31f;&#x1f31f;本期讲解关于HTMLCSSJavaScript的基础知识&#xff0c;小编带领大家简单过一遍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 …

【Python · PyTorch】卷积神经网络(基础概念)

【Python PyTorch】卷积神经网络 CNN&#xff08;基础概念&#xff09; 0. 生物学相似性1. 概念1.1 定义1.2 优势1.2.1 权重共享1.2.2 局部连接1.2.3 层次结构 1.3 结构1.4 数据预处理1.4.1 标签编码① One-Hot编码 / 独热编码② Word Embedding / 词嵌入 1.4.2 归一化① Min-…

机器学习-36-对ML的思考之机器学习研究的初衷及科学研究的期望

文章目录 1 机器学习最初的样子1.1 知识工程诞生(专家系统)1.2 知识工程高潮期1.3 专家系统的瓶颈(知识获取)1.4 机器学习研究的初衷2 科学研究对机器学习的期望2.1 面向科学研究的机器学习轮廓2.2 机器学习及其应用研讨会2.3 智能信息处理系列研讨会2.4 机器学习对科学研究的重…

激光slam学习笔记5---ubuntu2004部署运行fastlivo踩坑记录

背景&#xff1a;看看fastlivo论文&#xff0c;觉得挺有意思的&#xff0c;就本地部署跑跑看看效果。个人环境&#xff0c;ubuntu20.04。 一、概要 由于依赖比较多&#xff0c;个人构建工作空间&#xff0c;使用catkin_make编译 src├── FAST-LIVO├── livox_ros_driver…

多模态大模型开启AI社交新纪元,Soul App创始人张璐团队亮相2024 GITEX GLOBAL

随着AI在全球范围内的加速发展和广泛应用,各行业纷纷在此领域发力。作为全球最大的科技盛会之一,2024年的GITEX GLOBAL将目光再次聚焦于人工智能的飞速发展,吸引了超过6700家来自各个领域的企业参与。在这样的背景下,Soul App作为国内较早将AI技术应用于社交领域的平台,首次亮相…

67页PDF |埃森哲_XX集团信息发展规划IT治理优化方案(限免下载)

一、前言 这份报告是埃森哲_XX集团信息发展规划IT治理优化方案&#xff0c;报告中详细阐述了XX集团如何优化IT治理结构以适应新的要求。报告还分析了集团管控模式的变化&#xff0c;提出了六大业务中心的差异化管控策略&#xff0c;并探讨了这些变化对IT治理模式的影响。报告进…

基于java Springboot高校失物招领平台

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

[C++] 智能指针

文章目录 智能指针的使用原因及场景分析为什么需要智能指针&#xff1f;异常抛出导致的资源泄漏问题分析 智能指针与RAIIC常用智能指针 使用智能指针优化代码优化后的代码优化点分析 析构函数中的异常问题解决方法 RAII 和智能指针的设计思路详解什么是 RAII&#xff1f;RAII 的…

Git回到某个分支的某次提交

1.切换到需要操作的分支&#xff08;<branch-name>是分支名称&#xff09;。 命令如下&#xff1a; git checkout <branch-name> 2.获取代码的提交记录 。命令如下&#xff1a; git log 按q退出当前命令对话。 获取到某次提交或者合并的hash值&#xff08;下文…

掌握 Spring Boot 的最佳方法 – 学习路线图

在企业界&#xff0c;人们说“Java 永垂不朽&#xff01;”。但为什么呢&#xff1f;Java 仍然是开发企业应用程序的主要平台之一。大型公司使用企业应用程序来赚钱。这些应用程序具有高可靠性要求和庞大的代码库。根据Java开发人员生产力报告&#xff0c;62% 的受访开发人员使…

1. Django中的URL调度器 (项目创建与简单测试)

1. 创建 Django 项目 运行以下命令创建一个名为 blog_project 的 Django 项目&#xff1a; django-admin startproject blog_project2. 创建博客应用 Django 中&#xff0c;项目可以包含多个应用。创建一个名为 blog 的应用&#xff1a; cd blog_project python manage.py …

frp内网穿透介绍安装教程

文章目录 前言一、安装二、测试使用总结 前言 内网穿透&#xff08;Port Forwarding&#xff09;是将公网上的IP地址映射到内部网络中的一台计算机的某个端口上&#xff0c;以便外部网络可以访问该计算机中运行的应用程序。内网穿透技术可以通过一些开源工具来实现&#xff0c…

主界面获取个人信息客户端方

主界面获取个人信息客户端方 前言 上一集我们完成了websocket身份验证的内容&#xff0c;那么这一集开始我们将要配合MockServer来完成主界面获取个人信息的内容。 需求分析 我们这边是完成客户端那方的内容&#xff0c;当客户端登录成功之后&#xff0c;我们就要从服务器获…

redis实现消息队列的几种方式

一、了解 众所周知&#xff0c;redis是我们日常开发过程中使用最多的非关系型数据库&#xff0c;也是消息中间件。实际上除了常用的rabbitmq、rocketmq、kafka消息队列&#xff08;大家自己下去研究吧~模式都是通用的&#xff09;&#xff0c;我们也能使用redis实现消息队列。…