博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正。


算写设计模式的目的就是,首先自己可以理清思路,还有就是国内的设计模式资料很丰富,但是并没有专门用在游戏开发上的讲解,看过之后有些不知道怎么用在游
戏方面上,怎么用,博主在学习过程中会结合一些国外的游戏设计模式资料加上自己的理解与实践,写出文章,在自己理清思路的同时也希望能对像我这样的小白们
提供一点微薄帮助。

在我没学这个模式之前写的控制部分的代码,就是把按键控制写成if
else简单的,一次性的写在update()函数中,对这样散乱的块来做轮询,这样的代码不整体,是一次性的,难以维护,如果想要在其中新添加功能会非
常难找,改动也容易出现错误。之前的代码例子如下(错误示例,仅给出部分):

   void Update()
{
if (Input.GetKeyDown(KeyCode.J))
{
…攻击操作…
}
if (Input.GetKeyDown(KeyCode.Space))
{
…跳跃操作…
}
if (Input.GetKey(KeyCode.D))
{
…向左移动操作…
}
}

相信不少人和我一样是这么写的,接下来要介绍命令模式,这种模式不仅能用在unity的脚本编程上,所有面向对象语言都适用。

命令模式是游戏中很有用的设计模式,四人帮有一句话是这样说的:
Encapsulate
a request as an object, thereby letting users parameterize clients with
different requests, queue or log requests, and support undoable
operations.
大概意思是,将请求封装为一个对象,让用户参数化的提出不同的请求,并提供撤销操作。
我们可以理解为是把命令具体化来调用。
比如把控制命令变成实实在在的实体来调用,就不用像上面错误的例子那样轮询一堆散乱的东西。
如下图,我们先把具体控制封装成函数,J键定义为act操作,把space键定义为jump操作。
 
命令模式好处之一:
方便替换按键,大家都知道大部分游戏设置中会有替换按键的设置,把按键设置为自己的常用键,玩着顺手,使用命令模式我们把每个操作控制封装成块,把用户按键操作与实现控制通过命令解耦,更改按键十分容易。如下图:
 
我们通过赋值b1,b2可以动态改变按键操作。但是这样方便的一切的前提都是使用命令模式把命令具体化来调用。
再把所有button整合成一个数组button
然后代码就变成了这样

    void Update()
{
if (Input.GetKeyDown(button[0]))
{
act();
}
if (Input.GetKeyDown(button[1]))
{
jump();
}
if (Input.GetKey(button[2]))
{
move();
}
}

稍微规整了一些。而且这样我们就可以让玩家自定义按键了。
接下来我们就可以使用命令模式了,就是替换jump(),act(),move()这些操作为具体的command命令。
我们可以用一个借口command来抽象概括所有的这些命令,再分别实现它们。把这些操作看成客户下的命令,所以每一个command命令都应该有一个执行命令的函数execute()。代码如下:

using UnityEngine;
using System.Collections; public interface ICommand{
void execute();
}

这个接口就是我们的抽象,然后我们在一一实现我们的命令类,拿jump举例:

using UnityEngine;
using System.Collections; public class JumpCommand : ICommand
{
public void execute()
{
….实现jump….
}
}

但是这里出现了问题,jump命令是要用在我们的“hero”上的也就是玩家控制的人物,说白了就是一个
gameobject,所以只要把hero的gameobject传入我们的命令类即可。这样做带来了命令模式的另一个好处,举个博主最爱玩的游戏-传说
系列,里面一般有4-6个英雄可以替换控制(对,博主就是要安利你们玩。。),随意更换英雄,就是随意更换操作的人物,只要我们传参传入不同的hero
的gameobject即可。

using UnityEngine;
using System.Collections; public interface ICommand{
void execute(GameObject Hero);
}

然后这里我们可以选择是在一个新的脚本中实现操作,还是在这个command中实现,这两种都可以,前者重用性好,后者重用性差一些,但更具体。实例在一个新脚本HeroCtrl中实现具体操作:

using UnityEngine;
using System.Collections; public class JumpCommand : ICommand
{
public void execute(GameObject Hero)
{
Hero.GetComponent<HeroCtrl>().jump();
}
}

如果想在这个command中实现就可以这样:

using UnityEngine;
using System.Collections; public class JumpCommand : ICommand
{
float pow = 0.1f;
CharacterController controller;
public void execute(GameObject Hero)
{
controller = Hero.GetComponent<CharacterController>();
if (controller.isGrounded)
{
controller.Move(Vector3.up * pow);
Hero.GetComponent<Animation>().CrossFade(…);
}
}
}

此时的结构就是这样了,我们成功的实现了解耦:
 

我们在写AI的时候也可以使用命令模式,此时的AI逻辑只负责“发号施令”就可以了,更加灵活的编写AI。

后再次揭开sims的一个秘密,在玩sims可以对一个小人下许多命令,但是小人需要花时间才能干完一件事,通常我们看到我们想让他做的事的图标就会堆在
上面,小人会一个一个的处理,这就是任务列表,我们可以把代做的任务存到一个list中,给每个任务一个index,完成一个就做下一个,产生新任务就堆
在后面,这也是命令模式的一大功能。

然后就是撤销undo和重做redo部分,这个一般用在策略游戏中,我们错误操作了可以及时撤销。
undo实现方法就是把该任务反过来的操作作为undo函数,举一个最简单的例子:

public class addCommand : ICommand
{
public int execute(int num)
{
return ++num;
}
public int undo (int num)
{
return --num;
}
}

加法的undo就是减法,前移的undo就是后移。

如果想要redo的话就要把之前的操作储存起来,最好的办法把undo,当前操作,redo都存在一个堆中:
 

当产生一个新操作时,后面的redo全都不要了

游戏回放功能原理与之相同,回放时执行已经记录好的命令,有些游戏记录每一帧整个游戏的状态来回放,这样会消耗很多内存。

实现结果:
博主写了一个“玩具”用来专门练习设计模式,gameplay 模仿传说系列前几代。
命令模式完美运行:

命令模式完了。总之就是希望前辈们多多指正或者建议,能够带来帮助就更好。

博主近期渲染:最近用unity5弄的一些渲染

---- by wolf96

游戏开发设计模式之命令模式(unity3d 示例实现)的更多相关文章

  1. 游戏开发设计模式之原型模式 & unity3d JSON的使用(unity3d 示例实现)

    命令模式:游戏开发设计模式之命令模式(unity3d 示例实现) 对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现) 实现原型模式 原型模式带来的好处就是,想要构建生成任意独特对象的 ...

  2. 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)

    命令模式:游戏开发设计模式之命令模式(unity3d 示例实现) 对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现) 原型模式:游戏开发设计模式之原型模式 & unity3d ...

  3. 游戏开发设计模式之对象池模式(unity3d 示例实现)

    前篇:游戏开发设计模式之命令模式(unity3d 示例实现) 博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正. 原理:从一个固定 ...

  4. python设计模式之命令模式

    python设计模式之命令模式 现在多数应用都有撤销操作.虽然难以想象,但在很多年里,任何软件中确实都不存在撤销操作.撤销操作是在1974年引入的,但Fortran和Lisp分别早在1957年和195 ...

  5. 乐在其中设计模式(C#) - 命令模式(Command Pattern)

    原文:乐在其中设计模式(C#) - 命令模式(Command Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 命令模式(Command Pattern) 作者:webabcd ...

  6. 面向对象设计模式_命令模式(Command)解读

    在.Net框架中很多对象的方法中都会有Invoke方法,这种方法的设计实际是用了设计模式的命令模式, 模式图如下 其核心思路是将Client 向Receiver发送的命令行为进行抽象(ICommand ...

  7. 设计模式 ( 十三 ) 命令模式Command(对象行为型)

    设计模式 ( 十三 ) 命令模式Command(对象行为型) 1.概述         在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需 ...

  8. 折腾Java设计模式之命令模式

    博客原文地址 折腾Java设计模式之命令模式 命令模式 wiki上的描述 Encapsulate a request as an object, thereby allowing for the pa ...

  9. 用Java 8 Lambda表达式实现设计模式:命令模式

    在这篇博客里,我将说明如何在使用 Java 8 Lambda表达式 的函数式编程方式 时实现 命令 设计模式 .命令模式的目标是将请求封装成一个对象,从对客户端的不同类型请求,例如队列或日志请求参数化 ...

随机推荐

  1. 最新的C#SqlHelper 类苏飞修改版(转载)

    /// <summary> /// 类说明:公共的数据库访问访问类 /// 编码日期:2010-4-22 /// 编 码 人:苏飞 /// 联系方式:361983679 Email:[ur ...

  2. C#调用cmd程序,读取结果

    示例,调用cmd执行PING命令,读取结果,代码如下: using System; using System.Collections.Generic; using System.Linq; using ...

  3. Java文件操作二:File文件的方法

    一.文件的判断方法 判断方法 .boolean canExecute()判断文件是否可执行 .boolean canRead()判断文件是否可读 .boolean canWrite() 判断文件是否可 ...

  4. Gulp-入门教程 搭配环境

    之前一直听朋友谈起gulp,但没有使用过,最近有机会接触到,现在给大家分享下,不对的地方还请指正.我一直以为互相分享是学习的一种好方式.下面进入正题: 首先来了解下gulp,最起码要知道:我们为什么要 ...

  5. Child&ElementChild

    关于firstChild&firstElementChild及其同类属性使用,同时分析不同浏览器下child的包含的节点差异 <head> <meta charset=&qu ...

  6. js Module模式

    // 创建一个立即调用的匿名函数表达式// return一个变量,其中这个变量里包含你要暴露的东西// 返回的这个变量将赋值给counter,而不是外面声明的function自身 var counte ...

  7. MYSQL 不排序

    mysql: SELECT * FROM EVENT WHERE eventId IN(443,419,431,440,420,414,509) ORDER BY INSTR(',443,419,43 ...

  8. AS3.0定义变量的访问范围

    在AS3.0中变量的默认访问范围是:internal:包内成员可以访问,包外不可访问.AS2.0默认访问范围是public

  9. quick-x 触摸事件的新方法

    --[[ local function onTouch(event, x, y) print(event, x, y) if event == "began" then retur ...

  10. failure injection

    (1)malloc穷尽的情况: 假设下面的代码是测试代码,里面含有被测函数fmalloc,其中含有一个malloc语句,在一般情况下,是很难走到malloc失败的分支的,因为很难模拟系统内存耗尽的情况 ...