游戏开发设计模式之命令模式(unity3d 示例实现)

时间:2022-09-06 19:08:38

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


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

在我没学这个模式之前写的控制部分的代码,就是把按键控制写成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操作。
 游戏开发设计模式之命令模式(unity3d 示例实现)
命令模式好处之一:
方便替换按键,大家都知道大部分游戏设置中会有替换按键的设置,把按键设置为自己的常用键,玩着顺手,使用命令模式我们把每个操作控制封装成块,把用户按键操作与实现控制通过命令解耦,更改按键十分容易。如下图:
 游戏开发设计模式之命令模式(unity3d 示例实现)
我们通过赋值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(…);
}
}
}

此时的结构就是这样了,我们成功的实现了解耦:
 
游戏开发设计模式之命令模式(unity3d 示例实现)
我们在写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都存在一个堆中:
 游戏开发设计模式之命令模式(unity3d 示例实现)

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

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

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

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

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

---- by wolf96

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

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

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

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

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

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

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

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

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

  5. 乐在其中设计模式&lpar;C&num;&rpar; - 命令模式&lpar;Command Pattern&rpar;

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

  6. 面向对象设计模式&lowbar;命令模式&lpar;Command&rpar;解读

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

  7. 设计模式 &lpar; 十三 &rpar; 命令模式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. 微信支付开发&lpar;7&rpar; H5支付

    关键字:微信支付 微信支付v3 H5支付 wap支付 prepay_id 作者:方倍工作室原文: http://www.cnblogs.com/txw1958/p/wxpayv3_h5.html 本文 ...

  2. android 判断字符串是否为空与比对&lbrack;&quot&semi;&equals;&equals;&quot&semi;与equals()的区别&rsqb;

    if (s == null || s.equals("")) ; } s.equals("")里面是要比对的字符串 声明字符串未赋初始值或值,然后比对就会出错, ...

  3. PCL点云库:ICP算法

    ICP(Iterative Closest Point迭代最近点)算法是一种点集对点集配准方法.在VTK.PCL.MRPT.MeshLab等C++库或软件中都有实现,可以参见*中的ICP Alg ...

  4. &commat;Valid springMVC bean校验不起作用及如何统一处理校验

    SpringMVC 使用JSR-303进行校验 @Valid 使用注解 一.准备校验时使用的JAR validation-api-1.0.0.GA.jar:JDK的接口: hibernate-vali ...

  5. 终于解决&OpenCurlyDoubleQuote;Git Windows客户端保存用户名与密码”的问题

    这就是正确答案,我们已经验证过了,下面详细描述一下解决方法: 1. 在Windows中添加一个HOME环境变量,值为%USERPROFILE%,如下图: 2. 在“开始>运行”中打开%Home% ...

  6. Python----简单线性回归

    简单线性回归 1.研究一个自变量(X)和一个因变量(y)的关系   简单线性回归模型定义:y=β0+β1x+ε 简单线性回归方程:E(y)=β0+β1x 其中: β0为回归线的截距 β1为回归线的斜率 ...

  7. Oracle扩展的统计信息

    我们在收集列的统计信息与直方图时,往往都是对某一列的收集.当谓词使用多个相关列时,会导致约束条件的冗余.这几个相关的列也被称作关联列.出现这种情况时,查询优化器也会做出不准确的判断.所以我们必须对这些 ...

  8. Delphi编译选项

    编译选项的设置,称为“开关指令”,其中大部分值为布尔类型 一.代码生成(Code generation)1.Optimization  优化代码,默认true2.Stack frames  生成过程/ ...

  9. Jenkins入门,介绍、安装

    一.介绍 Jenkins: 三.安装 Jenkins:        第一种方式:        下载 windows 版本:进入 Jenkins 官网:https://jenkins.io/ 进入下 ...

  10. 学习CSS布局 - position例子

    position例子 通过具体的例子可以帮助我们更好地理解“position”.下面是一个真正的页面布局. 结果: 代码如下: <!DOCTYPE html> <html lang= ...