在项目中,会经常遇到这有的问题:
UI按钮点击,直接在onclick里写了一堆逻辑。
输入系统和角色行为强耦合。
想做撤销、重做、回放、AI自动操作时,发现代码全部绑定死了。
这时候,命令模式非常香了。
1.什么是命令模式?
将"请求"封装成对象,从而让你可以用不同的请求来参数化客户端,支持排队、日志、撤销等操作。
UML结构简化
Invoker(调用者) → Command(命令接口) → Receiver(执行者)
Invoke:按钮、输入系统、AI
Command:一次具体行为(Jump,Attack,UseItem)
Receiver:真正干活的对象(Player、Enemy、System)
2.为什么Unity项目特别适合命令模式?
(1)
cs
public void OnJumpBtnClick()
{
player.Jump();
}
耦合严重:UI必须知道Player,该逻辑要改UI,没法复用。
(2)支持撤销重做,比如回合制、编辑器工具、策略卡牌游戏。
(3)AI、自动操作、回放系统必备。
3.命令模式基础实现
(1)Command抽象接口
cs
public interface ICommand
{
void Execute();
void Undo(); // 可选
}
(2)Receiver(真正执行逻辑的对象)
cs
public class Player
{
public void Jump()
{
Debug.Log("Player Jump");
}
public void Crouch()
{
Debug.Log("Player Crouch");
}
}
(3)Concrete Command(具体命令)
cs
public class JumpCommand : ICommand
{
private Player _player;
public JumpCommand(Player player)
{
_player = player;
}
public void Execute()
{
_player.Jump();
}
public void Undo()
{
// 跳跃一般不能撤销,这里可以留空
}
}
public class CrouchCommand : ICommand
{
private Player _player;
public CrouchCommand(Player player)
{
_player = player;
}
public void Execute()
{
_player.Crouch();
}
public void Undo()
{
Debug.Log("Undo Crouch");
}
}
(4)Invoker(调用者)
cs
public class InputHandler
{
private ICommand _jumpCommand;
private ICommand _crouchCommand;
public InputHandler(Player player)
{
_jumpCommand = new JumpCommand(player);
_crouchCommand = new CrouchCommand(player);
}
public void HandleInput()
{
if (Input.GetKeyDown(KeyCode.Space))
_jumpCommand.Execute();
if (Input.GetKeyDown(KeyCode.C))
_crouchCommand.Execute();
}
}
4.UI绑定+命令模式
cs
public class UIButtonInvoker : MonoBehaviour
{
private ICommand _command;
public void SetCommand(ICommand command)
{
_command = command;
}
public void OnClick()
{
_command?.Execute();
}
}
//绑定时
var button = jumpBtn.GetComponent<UIButtonInvoker>();
button.SetCommand(new JumpCommand(player));
5.命令队列
cs
public class CommandQueue
{
private Queue<ICommand> _queue = new Queue<ICommand>();
public void Add(ICommand command)
{
_queue.Enqueue(command);
}
public void ExecuteAll()
{
while (_queue.Count > 0)
{
_queue.Dequeue().Execute();
}
}
}
6.撤销重做
cs
public class CommandHistory
{
private Stack<ICommand> _undoStack = new Stack<ICommand>();
public void Execute(ICommand command)
{
command.Execute();
_undoStack.Push(command);
}
public void Undo()
{
if (_undoStack.Count > 0)
_undoStack.Pop().Undo();
}
}