在 Unity 开发中, 命令模式(Command Pattern)的核心是将”请求”封装成独立的命令对象, 解耦请求”发送者”(如玩家输入\AI指令)和”接收者”(如角色\道具). 命令对象可以被存储\传递\撤销或重做, 还能支持批量执行(宏命令), 非常适合需要灵活控制操作流程的场景.
举个具体场景: 角色操作与撤销系统
假设你在开发一款2D平台游戏, 角色需要支持一下操作:
- 向做移动\向右移动
- 跳跃
- 攻击
传统写法会直接在输入管理器中调用角色的 \ 等方法, 导致输入逻辑和角色动作强耦合 ——— 如果后续要加”赊销上一步操作”(比如误触移动, 撤销回原来的位置)\”录制宏命令”(连续按3个键自动回放), 就需要大幅修改代码.
命令模式的解决思路: 把每个操作(移动\跳跃)封装成独立的”命令对象”, 输入管理器(发送者)只需”执行命令”, 角色(接收者)只需”响应命令”, 命令对象本身记录”执行/撤销”所需的状态(如移动前的位置).
命令模式的核心角色
- 抽象命令(Command): 定义命令的统一接口, 包含
Execute()(执行命令)和Undo()(撤销命令)方法.
- 具体命令(Concrete Command): 实现抽象命令, 持有”接收者”引用, 封装具体的请求逻辑(如调用者角色的移动方法), 并记录撤销所需的状态(如移动前的位置).
- 接收者(Receiver): 执行命令对应的实际操作(如角色的移动\跳跃逻辑), 不知道命令的存在, 只专注于自身功能.
- 调用者(Invoker): 触发命令执行的对象(如输入管理器), 负责创建命令\调用
Execute(), 还能管理命令历史(用于撤销/重做).
- 客户端(Client): 组装命令\接收者和调用者, 定义命令的执行流程(如绑定输入到命令)
代码实现步骤
- 定义接收者(角色, 执行实际行动)
先实现角色的核心功能(移动\跳跃\攻击), 它是命令的”执行者”, 不关心命令的逻辑:
- 定义抽象命令接口
所有具体命令都要实现这个接口, 统一”执行”和”撤销”的行为:
- 实现具体命令 (封装每个操作)
每个操作对应一个具体命令, 持有
PlayerController (接收者)的引用, 实现 Execute() 和 Undo() :- 实现调用者(输入管理器, 触发命令)
调用者负责监听输入\创建命令\执行命令, 还能管理命令历史栈(用于撤销/重做)
- 在Unity中配置与测试
- 创建角色: 新建 2D Sprite对象作为角色, 添加
Rigidbody2D(设置 Gravity Scale = 3, Freeze Rotation Z)\BoxCollider2D, 再添加PlayerController脚本. - 创建调用者: 新建空对象名为为
GameManager, 添加InputManager脚本, 将角色对象拖到_player字段 - 测试操作:
- 按 A/D:角色左右移动,命令入栈;
- 按 Space:角色跳跃;
- 按 J:角色攻击;
- 按 Z:撤销上一步操作(移动会恢复位置,跳跃会恢复速度);
- 按 R:自动执行 “右移→跳跃→攻击” 连招,3 秒后自动撤销。
命令模式的核心优势
- 解耦发送者和接收者:
InputManager(发送者)不需要知道PlayerController(接收者) 的MoveLeft()\Jump()方法, 只需要调用命令的Execute(), 后续修改角色动作或输入方式, 无需互相改动。
- 支持撤销 / 重做: 通过命令历史栈, 轻松实现 “撤销上一步” “重做下一步”(只需再加一个
redoStack存储撤销命令).
- 支持命令队列和宏命令: 可以批量执行多个命令(如连招\AI的一系列动作), 还能延迟执行(如定时触发命令).
- 扩展性强: 新增操作(如 “下蹲”) 只需新增
CrouchCommand类, 无需修改InputMnaager或PlayerController, 符合开放 - 封闭原则.
命令模式 vs 责任链模式
- 命令模式: 封装请求为对象, 重点是 “请求的存储 \ 执行 \ 撤销”, 发送者明确知道要执行哪个命令(但不关心接受者如何执行).
- 责任链模式: 请求沿链条传递, 重点是”找到能处理请求的接收者” 发送者不知道哪个接收者会处理.
在Unity中的其他应用场景
- AI行为树: AI的每个行为(如巡逻\ 攻击\ 逃跑)封装成命令, 通过行为树节点触发执行.
- 技能系统: 技能的释放\ 冷却\ 效果触发封装成命令, 支持技能连招录制\ 延迟释放
- 编译器工具: 自定义编辑器中的操作(如移动物体\删除资源) 封装成命令, 支持撤销/重做
总结: 命令模式就像”快递订单” ——— 你(发送者)下单(创建命令), 快递员(调用者)配送(执行命令), 商家(接收者)发货(实际动作). 订单本身可以被追踪(撤销)\批量处理(宏命令), 你不需要知道商家具体怎么发货, 只需要等待结果. 在Unity中, 它是实现灵活操作流程的”万能订单系统”