用依赖注射模式实现快速安全的游戏对象原型
|
英语原文:http://www.ic.uff.br/PosGraduacao/RelTecnicos/385.pdf (Fast and Safe Prototyping of Game Objects with Dependency) 译文原文:http://www.voidcn.com/article/p-rrluqhyk-eq.html
用依赖注射模式实现快速安全的游戏对象原型 Erick B. Passos Media Lab - UFF Jonhnny Weslley S. Sousa LCD - UFCG Giancarlo Nascimento Media Lab - UFF Esteban Walter Gonzales Clua Media Lab - UFF Lauro Kozovits UERJ ">[摘要]:大多数游戏引擎是基于游戏对象的继承和/或组件化的行为。虽然这种方法使得系统框架有一个清晰视图,良好的代码重用,快速原型化,它带来了一些问题,主要是游戏对象/组件的实例的高度依赖。这种依赖性往往导致静态转换和很难调试的空指针引用。本文应用依赖注入模式以安全地初始化游戏对象和减轻在游戏开发原型和产品发布阶段程序员角色职责。游戏对象的属性初始化依存关系注入只发生在初始化阶段,而在游戏循环中,没有任何性能损失。 [关键字]游戏引擎架构;依赖注射,对象组合 作者联系方式: {epassos,esteban}@ic.uff.br jonhnny@lsd.ufcg.edu.br giancarlotaveira@gmail.com lauro@jogos.etc.br
1.引言在计算机科学领域,很少有像电脑游戏和面向对象那样直接的编程映射关系。一个游戏类匹配几个不同的游戏类的实例存在的虚拟世界概念,游戏对象可以是任何实物例如一个人物,房屋或者是一个不可见的触发对象。游戏执行通常由游戏类内部具有以下三个目的的循环组成: 1.获取用户或网络输入, 2.根据用户输入或物理模拟更新游戏对象,播放动画和执行AI 3.在输出设备上绘制可见的游戏对象 虽然以上的循环形式可以更好地利用并行[邬斯迪摩赖斯扎米特等。2007],但我们认为,此基本模式能很好地反映我们工作的目的。为表模拟不同类型的对象,程序员通常创建的游戏对象上的子类,每一个新的游戏对象指定更专门的内容和行为。该继承方法存在的问题是众所周知的。这方面的一个很好的例子是,当两个不同层次的对象,有时有共同的功能和特点,导致代码冗余。这是在面向对象的软件设计一个共同的问题因此更换由游戏引擎继承为组合已经为一个很好的做法[福尔默2007年;斯托伊2006; Ponder2004; Billas 2002]。采用组合替代继承之后,游戏对象仅仅只需要拥有共同的属性如姓名,位置和方向。但最重要的是,它可以作为一种可重复使用的组件的容器。每一类扩展一个抽象组件描述一个游戏对象不同的的方面或行为如物理,AI,或生命值,根据需要来组成游戏对象。这些组件应该高度灵活,易于维护,同时也最大限度地提高代码重用,同时这些组件也可以用于不同的游戏类型。 这两种方法都有一个共同的问题,组件(或游戏对象)之间的高耦合性。举个例子来说,一个AI组件通常不但依赖于生命(Health)组件来做出决策同时也依赖于物理组件来运行。一般情况下,这些依赖通常由程序员直接解决。正如以下的java所示(部分AIComponent类,从最初的C++例子[stoy2006]改编) 1public void update(float interpolation){ 2final GameObjecto=getOwner(); 3Health h=(Health) o.getComponent("health"); 4if (h != null){ 5// take AIactionsbased onhealth 6} 7} Code 1: Traditional dependency handling 很容易看出此实现(AIComponent)显示的依赖于生命组件(第3行),该行代码假定相同的组件对象已经用标识“Health”在游戏对象中注册过。如果每个游戏对象都包含一个AI组件实例并且已经用Health组件初始化过,一切都会像预期的一样。显然,这段代码并没有什么不对,但进一步的审视将在下面给出: 当有必要清除隐式依赖时,代码2-4行只是空余的代码,与游戏逻辑的AIupdate无关 代码段第3行的显式转换,或者说在某些脚本语言中滥用强制转换,是在依赖对象或组件中一个常见的运行时问题 如果一个特定的游戏对象没有用Health组件初始化,那么AI决策将不会被正确执行,使得很难调试。 当设计关卡时,除非除非组件记录了这些讨人厌的依赖,不然程序员还得亲自写一些代码 一段等效UnrealScript代码[EpicGames 1998]将更加难以调试,因为该语言通过将指针转化为void*隐藏了所有的空指针和引用。Tim Sweeney最近说在Unreal引擎中大约百分之五十的Bug是因为缺乏强类型检查[Sweeney 2006].他也指出一个典型的游戏对象更新通常要涉及到五到十个其它对象,这也显示了依赖这个关系是多么的常见和明显。 本文应用依赖注射(Dependency Injection)模式来解决依赖的部分问题,通过本文,可以减轻程序员手工检查这些依赖的职责。正如以下章节将给出的一样,我们的框架解决安全初始化游戏对象或组件,使得编码以更加清晰和可维护的方式进行。本文剩下的章节由以下几个部分组成:第二部分讨论相关工作,第三部分描述概念和GCore框架实现的模式;而第四个部分将讲述依赖注射模式的使用和我们框架相对于前面研究的优点;最后,第五部分总结本文并概述今后的工作。 2.相关工作成功的商业游戏引擎对游戏对象继承有很强的依赖,例如CryEngine[Cry-Tek 2008]使用了类似于游戏对象和组件相似的实体和实体项。同样的架构也可以在其它商业引擎中找到,如Unreal Engine[EpicGames 1998]和Torque[GarageGames]。同时也有很多基于组件架构的引擎[UnityTechnologies 2008;Spinor;3DVia;Billias2002]。这些工具的一个共同问题是对象之间的依赖太高,这也是我们研究的潜在目标。在Tim Sweeney[Sweeney 2006]的一次谈话中,他暴露了当前编程语言和工具中中的两个问题细节:较差的并行处理和弱类型检查。他提出了许多新编程语言应该拥有的特征,这特征应该能解决程序员在实现游戏对象脚本时所遇到的大多数运行时错误。他的观点和我们的多少是有点相似的,但他的目标是创建一门拥有以上特征的新语言,这并不是一个简单的任务。在本文中,我们将使用当前流行的技术,这些技术可以应用到大多数工具中。我们的依赖注射实现是基于java的反射机制之上的,这种反射机制已经被作移植到基于如XNA[Microsoft]平台的C#上。C++和一些脚本语言是没有反射机制的,但是花一些时间来设计这样一系统是可能的[Pocomatic 2007]。Dungeon Siege是第一批包含完全游戏对象组件系统之一。在两界游戏开发者大会上[Billas2002;Billas2003],Scott Billas展示组件架构和一些帮助游戏开发的一些特征。在最近的交谈中[Bilas 2007],他展示了怎样提高游戏产品线的想法,它们其中的一些和游戏对象初始化的健全检查有关,例如属性需求和依赖。他提出,这些断言和和错误消息应该直接由组件程序员代替关卡设计师来实现。我们不但认识到这些想法确实非常重要,而且也提出用工具来做健全检查和依赖注射,代替程序员从而提高整个产品流水线。Haller et al[Haller et al.200]提出了使用通信槽和消息管理来消除强关联一种新的游戏对象和组件构架。该解决方案使得组合对象更容易和组件之间联系列容易,但是需要一个很复杂的架构。使用我们的方法,程序员根本不用学习新的语言和通信架构,因此这种方法更适合原型开发。Unity3D游戏引擎[UnityTechnologies 2008]是一个与其相关的最近产品,它获得了许多开发者的关注,因为它有设计得很好的游戏对象组件系统和场景编辑器,该编辑器还使用了一种可视化的方法来组合对象。在其最新版本中(2.1,在2008,7月下旬发布),一个简单的依赖健全检查形式已经提供给了脚本程序员,脚本程序员可以使用它来说明一个组件依赖于存在的另一个组件,这些都是在运行时在场景编辑器中检查。然而,尽管是自动的被场景编辑器初始化,这些实体并不是自动的注入依赖组件,而是由脚本程序员来完成,而且脚本程序员还要显示的调用getComponet(Type)方法来获取引用。我们的系统既做了健全检查也做到了自动注入依赖的组件。 据我们所知,所以之前的游戏对象组件系统研究只走了这么远,我们建议全面采用依赖注入来处理游戏引擎中组件依赖关系。在下面是的章节中,我们的的框架,GCore,将解释其架构和游戏对象组合的依赖注射。 3.GCore框架GCore,即Game Core的缩写,是一个数据驱动的游戏框架其目标是使用JMonkeyEngine [JMonkeyEngnie]生产高效游戏产品,JMonkeEngine是一个用OpenGL实现渲染OpenAL实现音频的场景图引擎,包括了一些例如物理引擎[JMEPhysic]和网络引擎子系统[Imagination]来实现一个可扩展和易用的工具。因为他的核心概念非常适合用其它平台和语言如C++,C#一实现,我们把关注点主要放在它的数据驱动和依赖注射功能上,这些功能使得游戏设计者,关卡设计者程序员和美术师在游戏产品线上合作更密切。程序员的角色是实现可复用的游戏组件而关卡设计师则整合组件和美术师作品。在本文中,我们将展示怎样用GCore工具和技术去帮助程序员和关卡设计师。 3.1主要概念从软件工程的观点来看,GCore定义了四个主要的概念/类来描述一个游戏:GameManager, GameState,GameObject,和AbstractComponent,GameManager是一个整个系统的外观模式[Gamma et al,1995]而GameState类似于Use-case图,每一个State都是一个独立的游戏场景(或者其他用户交互概念)例如3D游戏场景,菜单或者是HUD,在执行期间,玩家选择这些场景而游戏在这些不同的GameState实例之间切换。一个可运行的GameState由一个唯一的名字和一序列游戏对象(GameObject)组成,每个游戏对象又由一序列AbstractComponet实现组成。在图表一中,可以看到GCore的一个简单的类图架构,而图表2则用顺序图通过每一个不同的步骤方法调用用的顺序来解释了GCore中用到的游戏循环概念。 从图表2可以看出,每一个组件在每一帧频时都会被更新。在抽象类AbstractComponent中并没有渲染成员函数,因为它们中的大多数并不是一个具有图形属性的对象。相反,类GameObject维护了一个场景图结点,在需要时,该结点上的图形组件可以随时附加一个可以绘制的几何图元。在每一帧结束时,该结点都会被渲染,这样也使得该附加到该结点上的图形组件对象渲染到设备上。 3.2以数据驱的游戏对象组合(编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
