游戏开发设计模式之命令模式(unity3d 示例实现)
博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正。
打
算写设计模式的目的就是,首先自己可以理清思路,还有就是国内的设计模式资料很丰富,但是并没有专门用在游戏开发上的讲解,看过之后有些不知道怎么用在游
戏方面上,怎么用,博主在学习过程中会结合一些国外的游戏设计模式资料加上自己的理解与实践,写出文章,在自己理清思路的同时也希望能对像我这样的小白们
提供一点微薄帮助。
在我没学这个模式之前写的控制部分的代码,就是把按键控制写成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 示例实现)的更多相关文章
- 游戏开发设计模式之原型模式 & unity3d JSON的使用(unity3d 示例实现)
命令模式:游戏开发设计模式之命令模式(unity3d 示例实现) 对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现) 实现原型模式 原型模式带来的好处就是,想要构建生成任意独特对象的 ...
- 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)
命令模式:游戏开发设计模式之命令模式(unity3d 示例实现) 对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现) 原型模式:游戏开发设计模式之原型模式 & unity3d ...
- 游戏开发设计模式之对象池模式(unity3d 示例实现)
前篇:游戏开发设计模式之命令模式(unity3d 示例实现) 博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正. 原理:从一个固定 ...
- python设计模式之命令模式
python设计模式之命令模式 现在多数应用都有撤销操作.虽然难以想象,但在很多年里,任何软件中确实都不存在撤销操作.撤销操作是在1974年引入的,但Fortran和Lisp分别早在1957年和195 ...
- 乐在其中设计模式(C#) - 命令模式(Command Pattern)
原文:乐在其中设计模式(C#) - 命令模式(Command Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 命令模式(Command Pattern) 作者:webabcd ...
- 面向对象设计模式_命令模式(Command)解读
在.Net框架中很多对象的方法中都会有Invoke方法,这种方法的设计实际是用了设计模式的命令模式, 模式图如下 其核心思路是将Client 向Receiver发送的命令行为进行抽象(ICommand ...
- 设计模式 ( 十三 ) 命令模式Command(对象行为型)
设计模式 ( 十三 ) 命令模式Command(对象行为型) 1.概述 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需 ...
- 折腾Java设计模式之命令模式
博客原文地址 折腾Java设计模式之命令模式 命令模式 wiki上的描述 Encapsulate a request as an object, thereby allowing for the pa ...
- 用Java 8 Lambda表达式实现设计模式:命令模式
在这篇博客里,我将说明如何在使用 Java 8 Lambda表达式 的函数式编程方式 时实现 命令 设计模式 .命令模式的目标是将请求封装成一个对象,从对客户端的不同类型请求,例如队列或日志请求参数化 ...
随机推荐
- mvc在页面上显示PDF
今天看到需求要在页面上显示pdf,自己整了半天,啥效果都没有,偶尔有效果还各种不兼容,很无语的说.捣鼓了半天,没办法了,去谷歌了下,介绍了各种插件,各种方法,但是都挺繁琐的,本人不是一个很喜欢使用插件 ...
- C# -abstract, override, virtual, new
new声明的方法,当使用子类的类型来调用的时候,它会运行子类的函数,而如果类型是基类的话,被隐藏的基类函数会被调用. 而子类中函数使用override的时候,则当使用子类的类型来调用的是,它会运行子 ...
- 原生与jqueryDOM
总结与复习原生与jquery的DOM操作. 获取元素节点: $(".class") $("#id") $(".class div") $(& ...
- java中的包装类
每一个包装类都对应一种基本数据类型.包装类有:Integer.character.Byte.Short.Long.Floot.Double.Boolean这八种,分别对应的基本数据类型是:int.ch ...
- kaptcha小案例(转)
使用kaptcha生成验证码 kaptcha是一个简单好用的验证码生成工具,通过配置,可以自己定义验证码大小.颜色.显示的字符等等.下面就来讲一下如何使用kaptcha生成验证码以及在服务器端取出验证 ...
- C#程序中:如何向xml文件中写入数据和读取数据
xml文件作为外部信息存储文件使用简单,方便,其结构和表格略有相似,下面简单的说一下xml文件内容的读取 …… using System.Xml;using System.IO;namespace W ...
- 网页登陆校验码C#版代码
[DefaultProperty("Text")] [ToolboxData("<{0}:ValidateCode runat=server></{0} ...
- 【随记】关于List集合用Linq GroupBy分组过后的遍历小记
List<LeaderKaoQin> lstLeader = new List<LeaderKaoQin>();//一个List集合IGrouping<string, L ...
- mysql datestamp坑
每次更改行数据,该行第一个datestamp如不赋值,会自动更新为当前时间.赋值还要注意用下new Date(time).updated_at要写在created_at前面...
- #Leet Code# LRU Cache
语言:C++ 描述:使用单链表实现,HeadNode是key=-1,value=-1,next=NULL的结点.距离HeadNode近的结点是使用频度最小的Node. struct Node { in ...