设计模式在游戏设计中的应用_第1页
设计模式在游戏设计中的应用_第2页
设计模式在游戏设计中的应用_第3页
设计模式在游戏设计中的应用_第4页
设计模式在游戏设计中的应用_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Java设计模式论文完成人班级学号设计模式在游戏设计中的应用1. 程序设计目标与使用说明1.1前言设计模式在软件工程中有着广泛的应用,如java的MVC框架,用到了Observer模式,c#的LINQ,用到了Iterator模式,甚至连c+的STL中Iterator模式都有广泛的应用,但是我对这些软件工程方面的应用不是很感兴趣,我感兴趣的是设计模式在游戏开发中的应用,因此我尝试着使用Strategy模式和Abstract Factory模式来设计一个角色扮演游戏,来体验设计模式在游戏开发中的优势之处,由于我更善于使用c#编程,因此游戏将以c#为基础,代码运行环境是VS2010。我始终觉得,设计

2、模式的作用是为了方便开发,如果因为设计模式而使得开发变得复杂,那就得不偿失了,当然由于水平有限,我所能涉及到的只是游戏与设计模式应用的冰山一角,更深入的知识,需要我在以后的学习中不断的去深入和探究。1.2工程说明工程开发环境是vs2010 32位旗舰中文版,项目是c#控制台应用程序。打开项目需要先打开电子档中的RpgGame文件夹,双击RpgGame.sln打开打开工程后,具体的源代码在Program.cs中:2. 游戏具体实现在游戏开发的开始,我设定构建一个魔法世界的游戏,游戏中的角色有魔法师、战士、牧师三种职业,人物有人类、精灵两种种族。三种职业需要施展不同的魔法,因此需要用Strateg

3、y模式将角色行为和技能施展分离开来;而两个种族由于有着大同小异的外形,所以可以用Abstract Factory模式提炼出抽象的“身体”和具体的人类和精灵的实现。2.1.角色技能与Strategy模式1) 继承实现的问题在开始游戏之前,我为玩家设定三种职业:魔法师、战士、牧师。正如字面上所看到的,魔法师擅长使用魔法攻击,战士擅长使用物理攻击,而牧师则擅长治疗。根据面向对象编程思想,我需要先定义一个抽象类作为基类,将三个职业的实现作为子类,以实现代码的重用。对于抽象基类来说,首先要确定的是角色应该拥有的独立于职业的能力:Ø ShowInfo():显示角色基本信息,如魔法师显示:This

4、 is a magician.Ø Run():让角色行走,显示:Player is running.Ø Stay():让角色站立,显示:Player is standing.一般来说,设计时需要遵循这样的原则:1. 对于所有继承类都有,但是每个继承类的实现各不相同的方法,我们在基类中只给出定义,不给出实现,而在继承类中予以实现。换言之,就是在基类中定义一个抽象方法。2. 对于所有继承类都有,并且每个继承类的实现完全相同的方法,我们直接在基类中实现它,而由子类去继承,以实现代码重用。很显然,每个职业都拥有上边定义的三种能力,其中ShowInfo()对于每种职业都不相同,而Ru

5、n()和Stay()对于每种职业都相同,于是构建基类,实现下面的UML设计:2) 基类实现和继承实现的问题实现基本功能后,考虑让所有的职业可以实现技能的释放,因此需要添加一个新的方法ReleaseSkills(),很容易想到的是在基类Charactor中添加方法ReleaseSkills(),这样可以实现代码的重用,但是很容易就可以发现三个子类释放技能的方式并不相同,将方法放在基类中不但没有解决问题,反而使问题复杂了,将问题抽象化,可以得到这样的描述:给基类添加实体方法,使得不应该拥有此方法的子类也拥有了此方法,也使得所有子类方法拥有了完全一样的实现。当然可以有另外一种解决的方法,那就是在每个

6、子类中定义各自的ReleaseSkills()覆盖基类的方法,这样似乎解决了问题,但如果有一天需要加入一个新的职业,比如骑士,那就需要重新定义它的ReleaseSkills(),这样的缺陷显而易见:v 没有实现代码重用,如果新添加角色的话,就需要重新写代码进行覆盖。v 如果释放技能的方法需要改动,那么就不得不在子类中都重新写一遍。v 如果新定义一种职业不需要释放技能,但是它也继承了ReleaseSkills()的方法,即使在子类中定义一个空的ReleaseSkills()方法,仍然会暴露出它拥有ReleaseSkills()的能力。3) 引入接口继承的使用会带来很多问题,那么尝试使用接口来实现

7、。首先定义ReleaseSkills()接口,然后在子类中实现。当然这里没有考虑战士释放技能的问题,事实上战士一般是不需要释放技能的能力的。由此我们得到下面的UML图:使用接口虽然解决了部分问题,但是有更多的问题没有解决,例如,子类仍然要重新写自己的释放技能的实现代码,所以为了使释放技能与角色本身分离开来,就需要将释放技能的接口封装起来。4) 封装行为面向对象的一个原则是封装变化,在当前的情况中,ReleaseSkills()这个行为是不断变化的,那么在设计的过程中我就需要想办法将它封装起来。实现此接口的类不再是定义的角色的基类或者子类,而是专用于ReleaseSkills()这个行为的类。如

8、下图所示:在这里,接口的实现放在了它自己的继承体系中,而不是放到角色类里边,每一个实现接口的类完成一个特定的ReleaseSkills()的实现,比如ReleaseMagic类打印一句:Magic has been released,ReleaseLight类(光系魔法,也就是治疗魔法)打印一句:Light has been released,而ReleaseNothing类对应不释放魔法的战士,打印一句:I cant release magic。下面要做的就是将这个技能释放的体系与角色体系结合起来,具体就是在基类中声明Skills类型的变量,如下图所示:注意到 Character 类中仍有一

9、个ReleaseSkills()方法,但是这个方法与之前的ReleaseSkills()方法不同:² 它不是用来给子类去覆盖的。² 我们通过Character的ReleaseSkills()方法实际去调用 Skills 接口的ReleaseSkills()方法(实际上调用了其实体类的ReleaseSkills()方法)。具体类定义的代码如下:/定义释放技能的接口 public interface Skills void ReleaseSkills(); /定义释放魔法的类 public class ReleaseMagic : Skills public void Rele

10、aseSkills() Console.WriteLine("Magic has been released."); /定义释放治疗的类 public class ReleaseLight : Skills public void ReleaseSkills() Console.WriteLine("Light has been released."); /定义不释放技能的类 public class ReleaseNothing : Skills public void ReleaseSkills() Console.WriteLine("I

11、 can't release skills."); /角色基类 public abstract class Charactor protected Skills releaseSkills; /释放技能,通过接口来调用 public void ReleaseSkills() releaseSkills.ReleaseSkills(); /动态更改角色的释放技能能力 public void ChangeSkills(Skills newSkill) Console.WriteLine("I have Changed my skills."); release

12、Skills = newSkill; public void Run() Console.WriteLine("I am Running."); public void Stay() Console.WriteLine("I am standing."); abstract public void ShowInfo(); /显示角色信息 /定义魔法师 public class Magician : Charactor public Magician() /初始化来自基类的Skills变量 releaseSkills = new ReleaseMagic(

13、); public override void ShowInfo() Console.WriteLine("I am a Magician."); /定义战士 public class Warrior : Charactor public Warrior() releaseSkills = new ReleaseNothing(); public override void ShowInfo() Console.WriteLine("I am a Warrior."); /定义牧师 public class Priest : Charactor publ

14、ic Priest() releaseSkills = new ReleaseLight(); public override void ShowInfo() Console.WriteLine("I am a Priest."); 到现在可以有一个总结,上边封装的Skills接口,实际上使用了Strategy模式,正如Strategy模式的官方定义:Strategy模式定义了一系列的算法,将它们每一个进行封装,并使它们可以相互交换。Strategy模式使得算法不依赖于使用它的客户端。2.2人物构造与 Abstract Factory模式1) 面向实现的方式当设定完职业之后

15、,我需要设定角色的种族,为了简单起见,在这里我只设定了精灵和人类,当然如果以后要扩展的话,还可以加入矮人、兽人、魔族等种族。对比精灵和人类可以发现他们的相同和不同之处:l 都有头部、身躯和四肢,面部都有五官。l 精灵的耳朵、眼睛、皮肤和身材是异于人类的。由此可以假定角色身体由三部分组成:头部(Head)、身体(Body)、皮肤(Skin)。于是得到下面的设计:2) 抽象组成身体的实体类我发现这样做,每个角色与他的身体部件是牢牢绑定在一起的,每创建一个角色,我都需要为它先行创建所有其复合的类(组成身体的实体类,比如HumanHead)。按照面向对象的思想,我想到应该对这一过程进行封装,将创建角色

16、部件这件事委派给其他的类来完成。观察上图,我发现尽管角色不同,但它们都是由三个部分构成,所以,我所能想到的实现这一过程的第一步,就是对组成身体的实体类进行抽象,定义三个接口:Head、Body、Skin,代表身体的三个部分,并且让Human和Elf的实体类去实现这个接口:通过上图可以发现,发现尽管定义了接口,但是如果角色Human和Elf仍然与接口的实体类关联,那么效果与面向实现完全相同。而且还有一个问题是Human与Elf相似的地方很多,除了名字不同其余的完全一样,那么久可以将它们合并成一个类,起名叫Race(种族),设计再次变成下面这样:3) 创建工厂类到这里现在结构已经很完善了,我定义了

17、接口来解决问题,也没有为不同的角色创建多个不同的类,而只要在Race的构造函数中为代表身体部件的变量赋不同的值,就可以创建不同种族的角色。这时候,如果有一个类可以专门负责创建身体部件这件事,当想要创建角色的时候,将这个类传递给Race的构造函数就可以了。设定创建Human身体组成部分的类称作:HumanPartsFactory,创建Elf身体部分的类称作ElfPartsFacotry。那么它们应该是这样的:但是Race的构造函数只能接受一个HumanPartsFactory类型的参数,为了传递ElfPartsFactory,将不得不再添加一个接受ElfPartsFactory类型的构造函数。这

18、样显然不好,所以应该建立一个抽象的种族工厂:4) Abstract Factory设计模式上面做的这些,使用到了一个新的设计模式:Abstract Factory。它的正式定义是这样的:提供一个接口用于创建一系列相互关联或者相互依赖的对象,而不需要指定它们的实体类。最后完整的UML设计应该是这样子的:下面是代码实现:/定义构成身体部分的接口 public interface Head string name get; public interface Body string name get; public interface Skin string name get; /组成human的类

19、public class HumanHead : Head public string name get return "Human Head" public class HumanBody : Body public string name get return "Human Body" public class HumanSkin : Skin public string name get return "Human Skin" /组成Elf的类 public class ElfHead : Head public string

20、name get return "Elf Head" public class ElfBody : Body public string name get return "Elf Body" public class ElfSkin : Skin public string name get return "Elf Skin" /定义工厂接口 public interface RaceFactory Head CreateHead(); Body CreateBody(); Skin CreateSkin(); /定义human的身体

21、工厂类 public class HumanFactory : RaceFactory public Head CreateHead() return new HumanHead(); public Body CreateBody() return new HumanBody(); public Skin CreateSkin() return new HumanSkin(); /定义Elf的身体工厂类 public class ElfFactory : RaceFactory public Head CreateHead() return new ElfHead(); public Body

22、 CreateBody() return new ElfBody(); public Skin CreateSkin() return new ElfSkin(); /定义Race类 public class Race public Head head; public Body body; public Skin skin; public Race(RaceFactory raceFactory) head = raceFactory.CreateHead(); body = raceFactory.CreateBody(); skin = raceFactory.CreateSkin();

23、为了方便的调用以上定义的技能类和种族类,我需要建立一个Race类来包含上边两个类的实现,代码如下: class Player public Charactor career; public Race race; /为玩家初始化 public Player(Charactor charactor, Race playerRace) career = charactor; race = playerRace; 将两部分设计模式结合起来,我们就可以在实际中很方便的应用它们,例如如果我想定义一个玩家,职业是魔法师,种族是精灵,那么可以这样:/定义一个新的玩家,职业是魔法师,种族是精灵Player player = new Player(new Magician(), new Race(new ElfFactory();当需要展示这个玩家的信息的时候,直接调用方法就可以了:player.car

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论