文章目录
-
-
- [⌨️ 命令模式(Command Pattern)深度解析](#⌨️ 命令模式(Command Pattern)深度解析)
-
- 一、模式本质与核心价值
- 二、经典UML结构
- 三、Unity实战代码(可撤销的建造系统)
-
- [1. 定义命令接口与接收者](#1. 定义命令接口与接收者)
- [2. 实现具体命令](#2. 实现具体命令)
- [3. 命令管理器(Invoker)](#3. 命令管理器(Invoker))
- [4. 客户端使用](#4. 客户端使用)
- 四、模式进阶技巧
-
- [1. 宏命令(组合命令)](#1. 宏命令(组合命令))
- [2. 异步命令执行](#2. 异步命令执行)
- [3. 命令序列化](#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. 定义命令接口与接收者
csharp
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. 实现具体命令
csharp
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)
csharp
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. 客户端使用
csharp
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. 宏命令(组合命令)
csharp
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. 异步命令执行
csharp
public class AsyncMoveCommand : ICommand {
public async Task ExecuteAsync() {
await MoveCoroutine();
}
private IEnumerator MoveCoroutine() {
// 移动动画协程
}
}
3. 命令序列化
csharp
[System.Serializable]
public class SaveCommand : ICommand {
public string SaveData;
public void Execute() {
PlayerPrefs.SetString("Save", SaveData);
}
public void Undo() {
PlayerPrefs.DeleteKey("Save");
}
}
五、游戏开发典型应用场景
-
输入映射系统
csharppublic class InputSystem { private Dictionary<KeyCode, ICommand> _keyBindings = new(); public void Update() { foreach(var binding in _keyBindings) { if(Input.GetKeyDown(binding.Key)) { binding.Value.Execute(); } } } }
-
AI行为队列
csharppublic class AICommander { private Queue<ICommand> _actionQueue = new(); public void ScheduleAction(ICommand cmd) { _actionQueue.Enqueue(cmd); } void Update() { if(_actionQueue.Count > 0) { _actionQueue.Dequeue().Execute(); } } }
-
网络命令同步
csharppublic class NetworkCommand : ICommand { public void Execute() { if(PhotonNetwork.IsMasterClient) { photonView.RPC("RpcExecute", RpcTarget.All); } } [PunRPC] private void RpcExecute() { // 实际执行逻辑 } }
-
回放系统
csharppublic 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决策 |
八、最佳实践原则
-
接口最小化:保持命令接口简洁
-
不可变状态:命令参数应在构造时确定
-
原子操作:每个命令代表一个完整操作
-
安全撤销 :确保Undo操作的幂等性
csharppublic void Undo() { if(_isUndone) return; // 撤销逻辑 _isUndone = true; }
九、常见问题解决方案
Q1:如何处理命令依赖?
→ 实现命令版本控制
csharp
public class VersionedCommand : ICommand {
public int Version;
public bool IsCompatibleWith(int currentVersion) {
return Version <= currentVersion;
}
}
Q2:如何优化大量命令存储?
→ 使用增量压缩
csharp
public class DeltaCommand : ICommand {
private byte[] _deltaData;
public void Compress(CommandState fullState) {
// 计算差异并压缩
}
}
Q3:如何处理网络延迟?
→ 实现预测回滚
csharp
public class PredictiveMoveCommand : ICommand {
public void Execute() {
_predictedPosition = CalculatePrediction();
_serverPosition = NetworkSync();
if(_predictedPosition != _serverPosition) {
RollbackAndResync();
}
}
}
上一篇 【行为型之责任链模式】游戏开发实战------Unity灵活事件处理系统的架构核心
下一篇 【行为型之解释器模式】游戏开发实战------Unity动态公式解析与脚本系统的架构奥秘