然后为不同的游戏行为(移动,缩放,当然不限于这些操作,可以自己扩展 理论上所有对游戏的行为都可以)创建不同的命令:
//移动命令 public class MoveCommand : Command { public Vector2 startPos; public Vector2 endPos; //初始化 public MoveCommand(Vector2 pos) { endPos = pos; } //执行 public override void Execute(Player avator) { startPos = avator.transform.position; avator.Move(endPos); } //撤销 public override void Undo(Player avator) { avator.Move(startPos); } } //缩放命令 public class ScaleCommand : Command { public Vector3 startScale; public Vector3 endScale; //初始化 public ScaleCommand(Vector3 scale) { endScale = scale; } //执行 public override void Execute(Player avator) { startScale = avator.transform.localScale; avator.Scale(endScale); } //撤销 public override void Undo(Player avator) { avator.Scale(startScale); } }每个类包含构造函数(初始化用),除了Execute执行操作之外,还对每个行为提供了Undo撤销操作。
这样我们就有了可以存储和传输的“行为”,即MoveCommand和ScaleCommand的对象,我们可以在鼠标点击后相应的事件更换为其他的对象,即可实现配置的输入,但是我主要为了实现撤销操作,所以按键映射部分请自行探索(换映射的对象)。
接下来需要一个管理映射的类。这个类有两个资本操作,执行命令和撤销命令,(P.s 其实命令队列推荐用栈来实现,压栈和出栈更为方便,因为我对List比较习惯,所以直接选用了List)
public void ExecutiveCommand(Command command) { commandList.Add(command); command.Execute(avators[playerIndex]); Debug.Log("excute command!"); } public void UndoCommand() { if (commandList.Count > 0) { Command uCommand = commandList[commandList.Count - 1]; commandList.Remove(uCommand); uCommand.Undo(avators[playerIndex]); Debug.Log(commandList.Count + "undo command"); } }然后加入获取命令代码,InputHandler()返回我们的操作,update()中我们判断是否接受到命令,如果收到执行操作或撤销:
void Update() { currentCommand = InputHandler(); if (currentCommand != null) { ExecutiveCommand(currentCommand); } if (Input.GetKeyDown(KeyCode.Escape)) { UndoCommand(); } } Command InputHandler() { if (Input.GetMouseButtonDown(0)) { Vector2 endPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); return new MoveCommand(endPos); } if (Input.GetKeyDown(KeyCode.Space)) { float randomSize = Random.value * 5.0f; return new ScaleCommand(new Vector3(randomSize, randomSize, 0)); } return null; }效果如下:
其实命令模式作为一个容易被忽略的设计模式,他的应用远比我例子实现的要强大的多,简单列举一下命令模式在游戏中的应用场景:
更换按键映射
赛车中的影子系统
策略游戏中的悔棋
游戏中的回放功能
...
其实命令模式无非都是记录先前操作之后存储或是传递,在我们需要的时候拿来查找复现等等,实现起来并不复杂效果却很常用。在看看先前的那段话,是不是印象深多了:
==将一组行为抽象为对象,这个对象和其他对象一样可以被存储和传递,从而实现行为请求者与行为实现者之间的松耦合,这就是命令模式==。
小结命令模式虽然好用,但是就如同所有设计模式一样,都要用相应的正确场景才可以用,因为没有完美的设计模式,命令模式的一大缺点在于导致类数目的膨胀,就那上例来说,我们只需要一个脚本中的update()就可以实现的内容重新创建了三个文件四个类并且值的传递也更为复杂,更不用考虑更复杂的游戏了,但是他在扩展性方面留下了很大的余地,所以我们在需要时请仔细考虑他的合适性。
在这里借用毛星云的提炼总结,言简意赅:
GOF对命令模式的定义是,命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象,同时支持可撤消的操作。命令模式是回调机制的面向对象版本。
将一组行为抽象为对象,这个对象和其他对象一样可以被存储和传递,从而实现行为请求者与行为实现者之间的松耦合,这就是命令模式。
命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。