Unity学习之路(1):《游戏编程模式》-命令模式

《游戏编程模式》中的命令模式,翻阅了一天依旧是云里雾里,之后翻了几篇大牛的博客仔细拜读了一下才略有收货。

【游戏设计模式】之二 论撤消重做、回放系统的实现:命令模式 --by 浅墨_毛星云

原文是基于C++实现,尽管语言不是问题,但跨越语言和平台的隔阂还是有些困难。自己花了些时间简单实现了一下命令模式在Unity中的经典实现:撤销效果。这里写一些自己的心得体会。

命令模式

==“将一个请求封装成一个对象,从而允许你使用不同的请求,队列或日志将客户端参数化,同时支持请求操作的撤销与恢复”==

这是开篇第一段对于命令模式的定义,但是它太过于晦涩难懂,书中将他定义为

==命令是一个对象化(实例化)的方法调用==

但又太过笼统很难参透其中,我个人觉得毛星云在博客中的概括很合适:

==将一组行为抽象为对象,这个对象和其他对象一样可以被存储和传递,从而实现行为请求者与行为实现者之间的松耦合,这就是命令模式==。

解读一下这句话,当需要完成某个功能的时候,我们通常会调用相关类的方法来实现,这一调用的过程就是一个行为。这样的行为固然是正确的,但其实有一个考虑不全面的地方是事调用的结果会保留在反应对象之中,而并不会留下相应的工作记录。就好比我们调用了铅笔工具画了一条线,有调用钢笔画了另外一条线。但是之后我们在想当时是怎么画出的呢?要重现这一过程显然很困难。

如果我们不是调用方法,而是用一个命令来表示“请处理这份工作”,那么我们就可以调用命令在命令队列中,同时管理工作的历史记录,比如我想重复执行之前的操作,我想撤回甚至回访某个功能,就都可以实现了这样的设计模式,我们称之为Command模式,即命令模式。

也许我们没有察觉在大多数游戏中也有命令模式的影子,例如超级肉食男孩中的影子系统(同时回放多次游戏操作画面),赛车中的单人游戏的最高分影子系统,在线mmo中的死亡回放,精彩镜头等都和命令模式有着不解的渊源。

超级肉食男孩

命令模式的成名应用--撤销

接下来这个例子(撤销)就是命令模式的成名应用了。一个行为我们可以做(do),那我们就可以撤销(Undo),这一效果在策略游戏中最为常见,我们可以轻松回滚上一局的对战情形或者耍赖悔棋,从而允许玩家分析或尝试不同解法。当然这一效果可以应用到其他游戏产生不同的效果,是时候激发你的想象力了!

我们知道游戏操作一般都是通过鼠标,键盘等控制,而将这些控制信号转化为游戏反馈的是程序中的某个代码块,这些代码块记录游戏的输入,来产生相应的动作。在某款2D游戏中有一些“移动角色到某点”“把角色放大或缩小的魔法”等操作,我们在游戏中可以这样定义:

void Update() { //按下鼠标左键 if (Input.GetMouseButtonDown(0)) { Vector2 endPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); Move(endPos); } //按下空格键 else if (Input.GetKeyDown(KeyCode.Space)) { float randomSize = Random.value * 5.0f; Scale(new Vector3(randomSize, randomSize, 0)); } } //移动位置 public void Move(Vector2 end) { transform.position = end; } //随机缩放 public void Scale(Vector3 size) { transform.localScale = size; }

这是我们的主要代码段,代码定义了两种游戏行为,即点击屏幕移动和点击空格随机缩放大小。并在update函数中监控事件,检测到信号输入即响应对应的操作。从中心点开始我们进行三次移动,三次缩放操作,效果如下:

old

这就是将用户的输入硬编码到游戏的行为,这样的代码当然是有效的,但是这同样意味着一些后期可能会遇到的问题,我们知道许多游戏都允许玩家自定义按键映射,或要重现玩家先前的操作过程。这样的编码自然无法胜任。

为了后期扩展,我们可以应用命令模式重新定义一下我们的代码,把行为(例如上例中的点击移动)转化称“可以更换”的东西,就如同一个变量,这就是上文提到的把行为抽象成对象。具体操作如下:

创建新的脚本Command.cs,首先建立一个命令基类,其中Player是玩家脚本,也就是玩家想要移动或者缩放的对象,在玩家单一操作的游戏中只需要挂载到对应的物体上就可以省略了,但为了扩展一般我们就放在这里(好吧!其实是上次实验多人后懒得删= =):

//命令基类 public class Command { public virtual void Execute(Player avator) { } public virtual void Undo(Player avator) { } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfjxj.html