【行为型之命令模式】游戏开发实战——Unity可撤销系统与高级输入管理的架构秘钥

文章目录

      • ⌨️ 命令模式(Command Pattern)深度解析
        • 一、模式本质与核心价值
        • 二、经典UML结构
        • 三、Unity实战代码(可撤销的建造系统)
          • 1. 定义命令接口与接收者
          • 2. 实现具体命令
          • 3. 命令管理器(Invoker)
          • 4. 客户端使用
        • 四、模式进阶技巧
          • 1. 宏命令(组合命令)
          • 2. 异步命令执行
          • 3. 命令序列化
        • 五、游戏开发典型应用场景
        • 六、性能优化策略
        • 七、模式对比与选择
        • 八、最佳实践原则
        • 九、常见问题解决方案

⌨️ 命令模式(Command Pattern)深度解析

——以Unity实现可撤销操作智能输入系统为核心案例


一、模式本质与核心价值

核心目标
封装操作为对象,支持撤销/重做功能
解耦请求发送者与执行者,提升系统扩展性
✅ 支持请求队列日志记录,实现复杂操作管理

关键术语

  • Command(命令接口):定义执行操作的统一接口
  • ConcreteCommand(具体命令):实现具体业务逻辑
  • Invoker(调用者):触发命令执行(如输入处理器)
  • Receiver(接收者):实际执行操作的对象

数学表达
操作历史H可表示为命令序列:
H = [C₁, C₂, …, Cₙ]
撤销操作为:H.pop() → ExecuteInverse(Cₙ)


二、经典UML结构
«interface»
ICommand
+Execute()
+Undo()
MoveCommand
-_unit: Unit
-_from: Vector3
-_to: Vector3
+Execute()
+Undo()
InputHandler
-_commandHistory: Stack<ICommand>
+HandleInput()
Unit
+Move(Vector3)

三、Unity实战代码(可撤销的建造系统)
1. 定义命令接口与接收者
public interface ICommand {void Execute();void Undo();
}public class Builder : MonoBehaviour {public void BuildWall(Vector3 position) {Instantiate(wallPrefab, position, Quaternion.identity);}public void DestroyWall(Vector3 position) {var wall = Physics.OverlapSphere(position, 0.5f).FirstOrDefault(c => c.CompareTag("Wall"));if(wall != null) Destroy(wall.gameObject);}
}
2. 实现具体命令
public class BuildCommand : ICommand {private Builder _builder;private Vector3 _position;private GameObject _builtWall;public BuildCommand(Builder builder, Vector3 pos) {_builder = builder;_position = pos;}public void Execute() {_builder.BuildWall(_position);_builtWall = GameObject.FindWithTag("Wall");}public void Undo() {if(_builtWall != null) {_builder.DestroyWall(_builtWall.transform.position);}}
}
3. 命令管理器(Invoker)
public class CommandManager : MonoBehaviour {private Stack<ICommand> _commandHistory = new();private Stack<ICommand> _redoStack = new();public void ExecuteCommand(ICommand command) {command.Execute();_commandHistory.Push(command);_redoStack.Clear();}public void Undo() {if(_commandHistory.Count == 0) return;var cmd = _commandHistory.Pop();cmd.Undo();_redoStack.Push(cmd);}public void Redo() {if(_redoStack.Count == 0) return;var cmd = _redoStack.Pop();cmd.Execute();_commandHistory.Push(cmd);}
}
4. 客户端使用
public class BuildController : MonoBehaviour {[SerializeField] private Builder builder;[SerializeField] private CommandManager cmdManager;void Update() {if(Input.GetMouseButtonDown(0)) {var ray = Camera.main.ScreenPointToRay(Input.mousePosition);if(Physics.Raycast(ray, out var hit)) {var cmd = new BuildCommand(builder, hit.point);cmdManager.ExecuteCommand(cmd);}}if(Input.GetKeyDown(KeyCode.Z)) cmdManager.Undo();if(Input.GetKeyDown(KeyCode.Y)) cmdManager.Redo();}
}

四、模式进阶技巧
1. 宏命令(组合命令)
public class MacroCommand : ICommand {private List<ICommand> _commands = new();public void Add(ICommand cmd) => _commands.Add(cmd);public void Execute() {foreach(var cmd in _commands) cmd.Execute();}public void Undo() {foreach(var cmd in _commands.AsEnumerable().Reverse()) {cmd.Undo();}}
}
2. 异步命令执行
public class AsyncMoveCommand : ICommand {public async Task ExecuteAsync() {await MoveCoroutine();}private IEnumerator MoveCoroutine() {// 移动动画协程}
}
3. 命令序列化
[System.Serializable]
public class SaveCommand : ICommand {public string SaveData;public void Execute() {PlayerPrefs.SetString("Save", SaveData);}public void Undo() {PlayerPrefs.DeleteKey("Save");}
}

五、游戏开发典型应用场景
  1. 输入映射系统

    public class InputSystem {private Dictionary<KeyCode, ICommand> _keyBindings = new();public void Update() {foreach(var binding in _keyBindings) {if(Input.GetKeyDown(binding.Key)) {binding.Value.Execute();}}}
    }
    
  2. AI行为队列

    public class AICommander {private Queue<ICommand> _actionQueue = new();public void ScheduleAction(ICommand cmd) {_actionQueue.Enqueue(cmd);}void Update() {if(_actionQueue.Count > 0) {_actionQueue.Dequeue().Execute();}}
    }
    
  3. 网络命令同步

    public class NetworkCommand : ICommand {public void Execute() {if(PhotonNetwork.IsMasterClient) {photonView.RPC("RpcExecute", RpcTarget.All);}}[PunRPC]private void RpcExecute() {// 实际执行逻辑}
    }
    
  4. 回放系统

    public class ReplaySystem {private List<TimestampedCommand> _commandLog = new();public void Record(ICommand cmd) {_commandLog.Add(new TimestampedCommand(Time.time, cmd));}public void PlayReplay() {StartCoroutine(ReplayCommands());}
    }
    

六、性能优化策略
策略实现方式适用场景
命令池重用命令对象高频命令创建
批量处理合并相似命令大量小操作
延迟执行分帧处理命令队列性能敏感场景
二进制序列化优化存储空间回放/存档系统

七、模式对比与选择
维度命令模式策略模式
目的封装操作替换算法
状态管理支持撤销无状态
执行时机可延迟执行立即执行
典型应用输入处理AI决策

八、最佳实践原则
  1. 接口最小化:保持命令接口简洁
  2. 不可变状态:命令参数应在构造时确定
  3. 原子操作:每个命令代表一个完整操作
  4. 安全撤销:确保Undo操作的幂等性
    public void Undo() {if(_isUndone) return;// 撤销逻辑_isUndone = true;
    }
    

九、常见问题解决方案

Q1:如何处理命令依赖?
→ 实现命令版本控制

public class VersionedCommand : ICommand {public int Version;public bool IsCompatibleWith(int currentVersion) {return Version <= currentVersion;}
}

Q2:如何优化大量命令存储?
→ 使用增量压缩

public class DeltaCommand : ICommand {private byte[] _deltaData;public void Compress(CommandState fullState) {// 计算差异并压缩}
}

Q3:如何处理网络延迟?
→ 实现预测回滚

public class PredictiveMoveCommand : ICommand {public void Execute() {_predictedPosition = CalculatePrediction();_serverPosition = NetworkSync();if(_predictedPosition != _serverPosition) {RollbackAndResync();}}
}

上一篇 【行为型之责任链模式】游戏开发实战——Unity灵活事件处理系统的架构核心
下一篇 【行为型之解释器模式】游戏开发实战——Unity动态公式解析与脚本系统的架构奥秘

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

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

相关文章

计算机网络|| 路由器和交换机的配置

一、实验目的 1. 了解路由器和交换机的工作模式和使用方法&#xff1b; 2. 熟悉 Cisco 网络设备的基本配置命令&#xff1b; 3. 掌握 Cisco 路由器的基本配置方式及配置命令&#xff1b; 4. 掌握路由器和交换机的基本配置与管理方法。 二、实验环境 1. 运行 Windows 操作…

面试--HTML

1.src和href的区别 总结来说&#xff1a; <font style"color:rgb(238, 39, 70);background-color:rgb(249, 241, 219);">src</font>用于替换当前元素&#xff0c;指向的资源会嵌入到文档中&#xff0c;例如脚本、图像、框架等。<font style"co…

CVPR2025 | Prompt-CAM: 让视觉 Transformer 可解释以进行细粒度分析

Prompt-CAM: Making Vision Transformers Interpretable for Fine-Grained Analysis 摘要-Abstract引言-Introduction方法-Approach预备知识-PreliminariesPrompt-CAM: Prompt Class Attention Map特征识别与定位-Trait Identification and Localization变体与扩展-Variants an…

动态规划问题 -- 多状态模型(粉刷房子)

目录 动态规划分析问题五步曲题目概述代码编写 动态规划分析问题五步曲 不清楚动态规划分析问题是哪关键的五步的少年们可以移步到 链接: 动态规划算法基础 这篇文章非常详细的介绍了动态规划算法是如何分析和解决问题的 题目概述 链接: 粉刷房子 状态表示&#xff08;题目要求…

Spring Boot 注解详细解析:解锁高效开发的密钥

一、引言 Spring Boot 以其快速开发、自动配置等特性&#xff0c;成为构建 Java 应用程序的热门框架。而注解在 Spring Boot 中扮演着至关重要的角色&#xff0c;它们如同魔法指令&#xff0c;简化了配置流程&#xff0c;增强了代码的可读性与可维护性。本文将深入剖析 Spring…

【Python】抽象基类ABC

抽象基类(Abstract Base Classes)的核心作用 抽象基类(ABC)是Python中一种特殊的类&#xff0c;它通过abc模块实现&#xff0c;主要服务于面向对象编程中的接口规范和设计约束。以下是它的核心作用&#xff1a; 1. 强制接口实现&#xff08;核心作用&#xff09; 确保子类必…

[python] Python单例模式:__new__与线程安全解析

一 实例的创建过程 我们之前了解过在构造一个类的实例化对象时,会默认调用__init__方法&#xff0c;也就是类的初始化也叫构造函数&#xff0c;但其实在调用__init__方法前会首先调用__new__方法&#xff08;只有在py3新式类才有&#xff09;。即下面 __new__(): 创建实例 作…

笔记本电脑打开网页很慢,一查ip地址网段不对怎么处理

我有一个笔记本&#xff0c;在家里连WIFI后获取到的ip地址网段不对&#xff0c;那么常规做法是手动去配置个静态IP和DNS&#xff0c;要知道笔记本IP地址默认采用的是DHCP&#xff0c;也就是动态获取ip地址。如果手动设置静态IP&#xff0c;也就是固定IP的话&#xff0c;你换个场…

怎样将MM模块常用报表设置为ALV默认格式(MB52、MB5B、ME2M、ME1M等)

【SAP系统研究】 对SAP系统中的报表,最方便的格式就是ALV了,可排序、可导出,非常友好。 但有些常见报表却不是默认ALV界面的,譬如MB52: 是不是有点别扭?但其实是可以后台配置进行调整的。 现将一些常用报表修改为默认ALV的方法进行总结,便于大家使用。 一、MB52、MB5…

Redis——达人探店

达人探店 发布探店笔记 探店笔记类似点评网站的评价&#xff0c;往往是图文结合&#xff0c;对应的表有两个&#xff1a; 发布博文对应两个接口 案例&#xff1a;实现查看发布探店笔记的接口 需求&#xff1a;点击首页的探店笔记&#xff0c;会进入详情页面&#xff0c;实现…

Git初始化相关配置

Git配置 在Git安装完成后&#xff0c;windows操作系统上会多出一个Git Bash的软件&#xff0c;如果是linux或者是macOS&#xff0c;那么直接打开终端&#xff0c;在终端中敲击命令即可 # 检查git版本 git -v # 或 git --version在使用git时&#xff0c;需要配置一下用户名和邮…

MySQL JSON_ARRAYAGG 实现汇总+明细数据展示

一、业务场景 在投注记录查询功能中&#xff0c;我们需要展示每个彩票期号(userId lotteryIssue分组)的汇总数据&#xff08;总金额、总注数&#xff09;&#xff0c;同时也要显示该期号下的所有明细投注记录。 解决方案&#xff1a;JSON_ARRAYAGG MySQL 5.7 提供的 JSON_A…

【Lua】Redis 自增并设置有效期

【Lua】Redis 自增并设置有效期 方案一 每次执行都会更新有效期 EVAL "local current redis.call(INCRBY, KEYS[1], ARGV[1]);if tonumber(ARGV[2]) > 0 then redis.call(EXPIRE, KEYS[1], ARGV[2]) end;return current;" 1 mycounter 1 10 参数: 1 代表KEY…

CCF第七届AIOps国际挑战赛季军分享(RAG)

分享CCF 第七届AIOps国际挑战赛的季军方案&#xff0c;从我们的比赛经历来看&#xff0c;并不会&#xff0c;相反&#xff0c;私域领域问答的优秀效果说明RAG真的很重要 历经4个月的时间&#xff0c;从初赛赛道第1&#xff0c;复赛赛道第2&#xff0c;到最后决赛获得季军&…

YOLO v2:目标检测领域的全面性进化

引言 在YOLO v1取得巨大成功之后&#xff0c;Joseph Redmon等人在2016年提出了YOLO v2&#xff08;也称为YOLO9000&#xff09;&#xff0c;这是一个在准确率和速度上都取得显著提升的版本。YOLO v2不仅保持了v1的高速特性&#xff0c;还通过一系列创新技术大幅提高了检测精度…

Linux-Ubuntu安装Stable Diffusion Forge

SD Forge在Win上配置起来相对简单且教程丰富&#xff0c;而在Linux平台的配置则稍有门槛且教程较少。本文提供一个基于Ubuntu24.04发行版&#xff08;对其他Linux以及SD分支亦有参考价值&#xff09;的Stable Diffusion ForgeUI安装配置教程&#xff0c;希望有所帮助 本教程以N…

量子计算实用化突破:从云端平台到国际竞合,开启算力革命新纪元

在硅谷某生物医药实验室&#xff0c;研究员艾米丽正盯着量子计算模拟界面露出微笑 —— 搭载中电信 "天衍" 量子计算云平台的 880 比特超导量子处理器&#xff0c;用 17 分钟完成了传统超算需 3 个月才能跑完的新型抗生素分子键合模拟。这个场景标志着量子计算正从 &…

计算机操作系统(七)详细讲解进程的组成与特性,状态与转换

计算机操作系统&#xff08;七&#xff09;进程的组成与特性&#xff0c;状态与转换 前言一、进程的组成1. 什么是“进程”&#xff1f;2. 进程的三个核心组成部分2.1 PCB&#xff08;进程控制块&#xff09;—— 进程的“身份证户口本”2.2 程序段—— 进程的“任务清单”2.3 …

MapReduce基本介绍

核心思想 分而治之&#xff1a;将大规模的数据处理任务分解成多个可以并行处理的子任务&#xff0c;然后将这些子任务分配到不同的计算节点上进行处理&#xff0c;最后将各个子任务的处理结果合并起来&#xff0c;得到最终的结果。 工作流程 Map 阶段&#xff1a; 输入数据被…

Linux操作系统实战:中断源码的性能分析(转)

Linux中断是指在Linux操作系统中&#xff0c;当硬件设备或软件触发某个事件时&#xff0c;CPU会中断正在执行的任务&#xff0c;并立即处理这个事件。它是实现实时响应和处理外部事件的重要机制&#xff0c;Linux中断可以分为两种类型&#xff1a;硬件中断和软件中断&#xff0…