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

文章目录

⌨️ 命令模式(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");
    }
}

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

    csharp 复制代码
    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行为队列

    csharp 复制代码
    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. 网络命令同步

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

    csharp 复制代码
    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操作的幂等性

    csharp 复制代码
    public 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动态公式解析与脚本系统的架构奥秘

相关推荐
MZWeiei10 小时前
Spark SQL 运行架构详解(专业解释+番茄炒蛋例子解读)
大数据·分布式·sql·架构·spark
#金毛10 小时前
一、HAL库的设计理念详解:从架构到实践
stm32·嵌入式硬件·架构
是麟渊10 小时前
【大模型面试每日一题】Day 17:解释MoE(Mixture of Experts)架构如何实现模型稀疏性,并分析其训练难点
人工智能·自然语言处理·面试·职场和发展·架构
好吃的肘子10 小时前
MongoDB 高可用复制集架构
数据库·mongodb·架构
TsingtaoAI10 小时前
医疗系统开发架构和技术路线建议-湖南某三甲医院
架构·医疗ai系统·医疗信息化·医疗系统架构·医疗ai机构
m0_5557629010 小时前
D-Pointer(Pimpl)设计模式(指向实现的指针)
设计模式
小Mie不吃饭10 小时前
【23种设计模式】分类结构有哪些?
java·设计模式·设计规范
码上飞扬11 小时前
MongoDB数据库深度解析:架构、特性与应用场景
数据库·mongodb·架构
九章云极AladdinEdu13 小时前
GPU SIMT架构的极限压榨:PTX汇编指令级并行优化实践
汇编·人工智能·pytorch·python·深度学习·架构·gpu算力