GameDesign 板


LINE

※ 引述《cjcat2266 (CJ Cat)》之铭言: : 这是我上个月在PTT Flash板聚所分享的 : "组件式游戏引擎 (Component-Based Game Engine)" : 主要介绍组件式游戏引擎与继承式游戏引擎的差异 : 还有组件式游戏引擎的各项特色 : 使用的范例语言为ActionScript 3.0 : 在这里跟大家分享一下~ :) : https://www.youtube.com/view_play_list?p=472DB0C5A3976D80 这边刚好翻了一篇文章 http://wp.me/pBAPd-fj 主要就是说明这个概念.因为还有例子,讲得很清楚. 跟我在讨论会讲的题目有相关 但又有一些差别.我可能还要想再花点时间想清楚. 有兴趣的人可以直接end.抓最後的一些pdf跟投影片来参考. Evolve Your Hierarchy 元件式的重构 By Mick West http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/ Refactoring Game Entities with Components 用元件来重构游戏实体 Up until fairly recent years, game programmers have consistently used a deep class hierarchy to represent game entities. The tide is beginning to shift from this use of deep hierarchies to a variety of methods that compose a game entity object as an aggregation of components. This article explains what this means, and explores some of the benefits and practical considerations of such an approach. I will describe my personal experience in implementing this system on a large code base, including how to sell the idea to other programmers and management. 时至今日,游戏程式设计者往往使用深度的继承来表达游戏实体(译按我喜欢称作game object)。"趋势"渐渐转换为以各种方式把这个实体物件包装为元件的集成。本篇文章就 是在讨论这个方法,以及他的好处跟实际面。这篇文章还会描述个人实作这种系统的经历 ,包含如何推销这个想法给其他程式设计者与管理阶层。 GAME ENTITIES 游戏实体 Different games have different requirements as to what is needed in a game entity, but in most games the concept of a game entity is quite similar. A game entity is some object that exists in the game world, usually the object is visible to the player, and usually it can move around. 在不同的游戏中游戏实体可以有不同的需求,但是概念上是类似的。游戏实体就是存在於 游戏世界的某一个物件,通常可以被玩家看到,也通常可以移动。 (略) 如清单所述游戏实体可以包含:飞弹,车辆,坦克,手榴弹,枪枝,英雄,行人,外星人 ,钢铁人飞行装,医疗装备,石头。 (略) 游戏实体可以有以下的功能:执行script,移动,被物理引擎推动,发出粒子,发出区域 音效,被玩家拾取,被玩家损害,爆炸,磁力反应,被玩家瞄准,跟随路径,播放动画。 TRADITIONAL DEEP HIERARCHIES 传统的深度继承 The traditional way of representing a set of game entities like this is to perform an object-oriented decomposition of the set of entities we want to represent. This usually starts out with good intentions, but is frequently modified as the game development progresses - particularly if a game engine is re-used for a different game. We usually end up with something like figure 1, but with a far greater number of nodes in the class hierarchy. 传统表示游戏实体集合的方式大多遵循物件导向分解的概念。概念上一开始都是好意。但 是随着游戏继续开发或是把就程式想办法套到新游戏上的时候,就会发现问题。这个传统 方式通常会变成像图1一样,有着大量数目的类别继承。 As development progresses, we usually need to add various points of functionality to the entities. The objects must either encapsulate the functionality themselves, or be derived from an object that includes that functionality. Often, the functionality is added to the class hierarchy at some level near the root, such as the CEntity class. This has the benefit of the functionality being available to all derived classes, but has the downside of the associated overhead also being carried by those classes. 随着开发进行,我们通常会增加很多的功能到这些实体上,要不是直接装到这些物件上, 就是要从其他物件那边继承这些功能过来。通常这些功能是在接近root的地方,好处是大 家都能共享这个功能。但是在底层的新物件就会被层层这些重担所包覆。 Even fairly simple objects such as rocks or grenades can end up with a large amount of additional functionality (and associated member variables, and possibly unnecessary execution of member functions). Often, the traditional game object hierarchy ends up creating the type of object known as "the blob". The blob is a classic "anti-pattern" which manifests as a huge single class (or a specific branch of a class hierarchy) with a large amount of complex interwoven functionality. 即便是石头或是手榴弹这种简单物件都会有一卡车继承而来的函式跟无关紧要的变数。通 常我们把这种情况叫做blob(像一团的大型物件)。这种blob通常不是一个好的设计模式, 因为他代表了一个巨大的单一类别或是继承的集合,包含了一堆交杂的函式。 While the blob anti-pattern often shows up near the root of the object hierarchy, it will also show up in leaf nodes. The most likely candidate for this is the class representing the player character. Since the game is usually programmed around a single character, then the object representing that character often has a very large amount of functionality. Frequently this is implemented as a large number of member functions in a class such as CPlayer. 这种blob不只会出现在继承的根部(root),也会出现在继承的底部(leaf)。最常见的 这种blob就是玩家人物物件,因为我们的游戏都是围绕着玩家角色来进行。所以通常被命 名为CPlayer的玩家人物物件就有一卡车函式。 The result of implementing functionality near the root of the hierarchy is an overburdening of the leaf objects with unneeded functionality. However, the opposite method of implementing the functionality in the leaf nodes can also have unfortunate consequence. Functionality now becomes compartmentalized, so that only the objects specifically programmed for that particular functionality can use it. Programmers often duplicate code to mirror functionality already implemented in a different object. Eventually messy re-factoring is required by re-structuring the class hierarchy to move and combine functionality. 在根部出现的这种blob就会导致底部的物件有很多无用处的函式。然而若是把这些函式实 做在底部物件,却又会发生功能无法透过继承共享的现象(导致再不同的底部物件中重复 作相同的功能)。最後就会发生必须重构的情况发生。 Take for example the functionality of having an object react under physics as a rigid body. Not every object needs to be able to do this. As you can see in figure 1, we just have the CRock and the CGrenade classes derived from CRigid. What happens when we want to apply this functionality to the vehicles? You have to move the CRigid class further up the hierarchy, making it more and more like the root-heavy blob pattern we saw before, with all the functionality bunched in a narrow chain of classes from which most other entity classes are derived. 举例来说,若是有一个会受到物理引擎影响的固体类别(CRigid),继承之後变成石头或 手榴弹。若是现在要加一个车辆物件到这个CRigid的继承树上时就会变得这个CRigid必须 往继承的根部移动,让他有更多能够包含车辆使用的功能。慢慢地他就变成所谓的blob。 AN AGGREGATION OF COMPONENTS 元件的集成 The component approach, which is gaining more acceptance in current game development, is one of separating the functionality into individual components that are mostly independent of one another. The traditional object hierarchy is dispensed with, and an object is now created as an aggregation (a collection) of independent components. 元件的方式慢慢被游戏开发所接受,是一种把功能区别变成单一互相独立元件的方法。传 统的继承被扬弃,物件变成一个元件的集成。 Each object now only has the functionality that it needs. Any distinct new functionality is implemented by adding a component. 每个物件只有他所需要的功能,每个新功能就只需实做成一个元件。 A system of forming an object from aggregating components can be implemented in one of three ways, which may be viewed as separate stages in moving from a blob object hierarchy to a composite object. 若是要把一个已经成为blob的继承系统变成元件式的系统,我们可以用三种方法来进行重 构。 OBJECT AS ORGANIZED BLOB 组织过的blob A common way of re-factoring a blob object is to break out the functionality of that object into sub-objects, which are then referenced by the first object. Eventually the parent blob object can mostly be replaced by a series of pointers to other objects, and the blob object's member functions become interface functions for the functions of those sub-objects. 最常见的重构blob物件的方式就是把这些物件依照功能分成小的物件,然後被原来的物件 所使用。最终这个blob就可以被一堆指到其他小物件的指标所取代。这个blob的物件就变 成一个使用其他小物件功能的介面(interface)。 This may actually be a reasonable solution if the amount of functionality in your game objects is reasonably small, or if time is limited. You can implement arbitrary object aggregation simply by allowing some of the sub-objects to be absent (by having a NULL pointer to them). Assuming there are not too many sub-objects, then this still allows you the advantage of having lightweight pseudo-composite objects without having to implement a framework for managing the components of that object. 若是时间有限,或是要重构的物件还算小的时候,这种方法是最合理的方式。这种方式可 以随意的组成你要的物件,尤其是当你不想要某一个功能的时候,就把那个小物件的指标 设为NULL即可。甚至不用实做整个管理物件的系统,很快的就可以建立一个虚拟的组合物 件出来。 The downside is that this is still essentially a blob. All the functionality is still encapsulated within one large object. It is unlikely you will fully factor the blob into purely sub-objects, so you will still be left with some significant overhead, which will weight down your lightweight objects. You still have the overhead of constantly checking all the NULL pointers to see if they need updating. 但是实际上这仍是一个所有功能都被包住的blob。只是你必须用是否为NULL的方式检查那 些功能有没有装进来。 OBJECT AS COMPONENT CONTAINER 元件的容器 The next stage is to factor out each of the components (the "sub-objects" in the previous example) into objects that share a common base class, so we can store a list of components inside of an object. 下一步就是打造每一个元件(先前说的小物件)为继承一个共通基底的物件,然後我们就 可以在物件里面用一个串列把他用得到的元件都串起来。 This is an intermediate solution, as we still have the root "object" that represents the game entity. However, it may be a reasonable solution, or indeed the only practical solution, if a large part of the code base requires this notion of a game object as a concrete object. 这是一个中间层的解法,我们仍有一个根部的物件来衍生我们的游戏实体。但这可能是唯 一有用且合理的解决方案。 Your game object then becomes an interface object that acts as a bridge between the legacy code in your game, and the new system of composite objects. As time permits, you will eventually remove the notion of game entity as being a monolithic object, and instead address the object more and more directly via its components. Eventually you may be able to transition to a pure aggregation. 我们的游戏物件就会变成一个介於原本程式与新的组合元件的介面。只要时间允许,最终 就会移除这一个原本是庞大整块的游戏实体,变为使用这个物件透过他的元件。然後就可 以转为一个单纯的集合体(pure aggregation)。 OBJECT AS A PURE AGGREGATION 单纯的集合体 In this final arrangement, an object is simply the sum of its parts. Figure 2 shows a scheme where each game entity is comprised of a collection of components. There is no "game entity object" as such. Each column in the diagram represents a list of identical components, each row can be though of as representing an objects. The components themselves can be treated as being independent of the objects they make up. 最後的布置会像这样,一个物件就是他元件的总和。图2展示了一个每一个游戏实体是由 元件组成的结构。没有所谓游戏实体物件这样的东西,每一栏都表示了单一的元件,每一 列表示独立的物件。元件终於可以独立於物件之外。 PRACTICAL EXPERIENCE 实际的实做经验 I first implemented a system of object composition from components when working at Neversoft, on the Tony Hawk series of games. Our game object system had developed over the course of three successive games until we had a game object hierarchy that resembled the blob anti-pattern I described earlier. It suffered from all the same problems: the objects tended to be heavyweight. Objects had unnecessary data and functionality. Sometimes the unnecessary functionality slowed down the game. Functionality was sometimes duplicated in different branches of the tree. 作者在Neversoft的Tony Hawk游戏系列中实做了一个元件集合的系统。在那之前专案已经 延续了三代的游戏,年代久远的继承架构就变成先前提到的blob。造成那些令人困扰的问 题:物件很大;物件有不相关的资料与功能;不相关的功能减缓游戏执行的速度;功能重 复的在不同的继承树上被实做。 I had heard about this new-fangled "component based objects" system on the sweng-gamedev mailing list, and decided it sounded like a very good idea. I set to re-organizing the code-base and two years later, it was done. 作者那时在sweng-gamedev新闻群组听到了这种新奇的元件式的物件系统,因此花了两年 去重新组织这个程式。 Why so long? Well, firstly we were churning out Tony Hawk games at the rate of one per year, so there was little time between games to devote to re-factoring. Secondly, I miscalculated the scale of the problem. A three-year old code-base contains a lot of code. A lot of that code became somewhat inflexible over the years. Since the code relied on the game objects being game objects, and very particular game objects at that, it proved to be a lot of work to make everything work as components. 为什麽花了这麽长的时间才完成?首先Tony Hawk游戏系列进入每年一款的量产,以至於 每次都只有一点时间来作重构。第二,错估了这个问题的大小。一个跑了三年的程式基底 包含了一堆不弹性的程式码,每个物件都很独特,难以解构。 EXPECT RESISTANCE 碰到抗拒 The first problem I encountered was in trying to explain the system to other programmers. If you are not particularly familiar with the idea of object composition and aggregation, then it can strike you as pointless, needlessly complex, and unnecessary extra work. Programmers who have worked with the traditional system of object hierarchies for many years become very used to working that way. They even become very good at working that way, and manage to work around the problems as they arise. 第一个作者碰到的问题就是如何说服这个系统给其他的程式设计人员。假如用不是很熟这 个物件组合与集成的问题的态度去说服其他人,通常会终结於没有意义;不需要这麽复杂 ;不想增加其他工作等回应。程式设计师已经很习惯於传统的继承写作方式。甚至很用的 很好。 Selling the idea to management is also a difficult. You need to be able to explain in plain words exactly how this is going to help get the game done faster. Something along the lines of: "Whenever we add new stuff to the game now, it takes a long time to do, and there are lots of bugs. If we do this new component object thing, it will let us add new stuff a lot quicker, and have fewer bugs." 推销这个想法给管理阶层也是一个困难。这需要能够用简单的文字去表达这样的新方法是 如何能够加速游戏的完成,就像这样说:"每当我们再游戏中新增一个元素,总是要花很 多时间,同时产生很多错误。假如我们采用元件式的方式,就会让我们更快,更少错误。 " My approach was to introduce it in a stealth manner. I first discussed the idea with a couple of programmers individually, and eventually convinced them it was a good idea. I then implemented the basic framework for generic components, and implemented one small aspect of game object functionality as a component. 作者推销的作法是采用低调的作法。先分别对一些程式设计师闲聊这个想法,说服他们这 是个好方法。实做这个架构的基础工作,且修改一个游戏物件为元件式作例子。 I then presented this to the rest of the programmers. There was some confusion and resistance, but since it was implemented and working there was not much argument. 然後展示给剩余的程式设计师,有些人会有疑惑跟抗拒,但既然已经有实做的结果了,就 没有太大的争论。 SLOW PROGRESS 步调缓慢 Once the framework was established, the conversion from static hierarchy to object composition happened slowly. It is thankless work, since you spend hours and days re-factoring code into something that seems functionally no different to the code it replaces. In addition, we were doing this while still implementing new features for the next iteration of the game. 基础的架构已经被建立之後,转换原本的继承架构为元件集合是的过程却十分的缓慢。这 是份不被感激的工作,因为只是花时间重作那些一样功能的东西。更糟的是,同时间还在 开发下一代游戏的功能。 At an early point, we hit the problem of re-factoring our largest class, the skater class. Since it contained a vast amount of functionality, it was almost impossible to re-factor a piece at a time. In addition, it could not really be re-factored until the other object systems in the game conformed to the component way of doing things. These in turn could not be cleanly refactored as components unless the skater was also a component. 早期的重点就是解构那个最大的类别-滑板者(skater)。他包含了庞大的功能根本不可 能一口气重构为元件。甚至是必须在其他物件都遵循元件式的架构後才能进行。也就是说 skater这个类别要先变成元件式才能重构其他元件。 The solution here was to create a "blob component." This was a single huge component, which encapsulated much of the functionality of the skater class. A few other blob components were required in other places, and we eventually shoehorned the entire object system into a collection of components. Once this was in place, the blob components could gradually be refactored into more atomic components. 解法就是创造一个blob的元件。一个超大的元件,包含了skater的所有功能。当然其他 blob也这样作。最後终於硬塞了整个物件系统到这个元件集合里面。一旦这步骤做完, blob元件就可以被解构为简单的元件。 RESULTS 结果 The first results of this re-factoring were barely tangible. But over time the code became cleaner and easier to maintain as functionality was encapsulated in discreet components. Programmers began to create new types of object in less time simply by combining a few components and adding a new one. 第一次重构的结果看起来没有什麽显着帮助。但是当功能被塞成离散的元件,程式越来越 清楚简单。 We created a system of data-driven object creation, so that entirely new types of object could be created by the designers. This proved invaluable in the speedy creation and configuration of new types of objects. 建立的系统是资料驱动的物件创造系统,因此整个新的物件能够由设计人员来创造,使得 开发速度跟调整变得十分快速。 Eventually the programmers came (at different rates) to embrace the component system, and became very adept at adding new functionality via components. The common interface and the strict encapsulation led to a reduction in bugs, and code that that was easier to read, maintain and re-use. 程式设计人员最终爱上了这种元件系统,也非常适应以元件的方式增加功能。这种设计方 式使得错误率降低,程式可读性上升,容易维护与重新使用。 IMPLEMENTATION DETAILS 实作的细节 Giving each component a common interface means deriving from a base class with virtual functions. This introduces some additional overhead. Do not let this turn you against the idea, as the additional overhead is small, compared to the savings due to simplification of objects. 元件有着共通的介面意思是在继承的基底类别有一些虚拟函式。在建构这部份的时候花了 不少精神。但别让这步骤阻止了重构,因为重构的结果会带来相对很多在开发上的省力。 Since each component has a common interface, it is very easy to add additional debug member functions to each component. That made it a relatively simple matter to add an object inspector that could dump the contents of the components of a composite object in a human readable manner. Later this would evolve into a sophisticated remote debugging tool that was always up to date with all possible types of game object. This is something that would have been very tiresome to implement and maintain with the traditional hierarchy. 既然元件都是相同介面,当然非常容易增加相同除错的架构上去。用可读的方式把资料 dump出来看。而且可以与复杂的远端除错工具结合,同时保持可以除错最新的物件类别。 若是用继承的方式是十分难以作到的。 Ideally, components should not know about each other. However, in a practical world, there are always going to be dependencies between specific components. Performance issues also dictate that components should be able to quickly access other components. Initially we had all component references going through the component manager, however when this started using up over 5% of our CPU time, we allowed the components to store pointers to one another, and call member functions in other components directly. 原则上,元件彼此之间应该互相不知道。然而实际应用时,某些元件总是必须相依彼此。 效能的考量使得应该要让元件彼此能够互相沟通。最初我们透过元件管理器 reference住 全部的元件,然而这管理器竟然消耗了百分之五的处理时间。因此我们允许元件储存其他 元件的指标直接呼叫成员函式。 The order of composition of the components in an object can be important. In our initial system, we stored the components as a list inside a container object. Each component had an update function, which was called as we iterated over the list of components for each object. 建构元件的顺序是重要的,在初始的系统中,我们用串列把元件储存在容器里。每一个元 件有自己的更新函式,然後走访串列一起更新他们。 Since the object creation was data driven, it could create problems if the list of components is in an unexpected order. If one object updates physics before animation, and the other updates animation before physics, then they might get out of sync with each other. Dependencies such as this need to be identified, and then enforced in code. 既然物件的创造是由资料所驱动,可能会因为再串列上的顺序导致不预期的问题。譬如说 某些流程应该比某些流程先更新。这种相依性必须考虑进去。 CONCLUSIONS 结论 Moving from blob style object hierarchies to composite objects made from a collection of components was one of the best decisions I made. The initial results were disappointing as it took a long time to re-factor existing code. However, the end results were well worth it, with lightweight, flexible, robust and re-usable code. 从继承的blob风格改为元件组合式的方式是作者的成功经验之一。最初因为花了很多时间 所以有点失望。然而最後的结果却相当值得,轻量,有弹性,通用,又能重复利用。 Resources 其他资源 Scott Bilas: GDC 2002 Presentation: A Data-Driven Game Object System http://www.drizzle.com/~scottb/gdc/game-objects.htm Bjarne Rene: Component Based Object Management. Game Programming Gems 5, 2005, page 25. Kyle Wilson: Game Object Structure: Inheritence vs Aggregation, 2002, http://www.gamearchitect.net/Articles/GameObjects1.html other reference : Game Object Structure: Inheritance vs. Aggregation ( 2002 ) By Kyle Wilson http://www.gamearchitect.net/Articles/GameObjects1.html Ogre Wiki : Architecture and Design in Games - A list of various must-read articles http://www.ogre3d.org/tikiwiki/Architecture+and+Design+in+Games -- "May the Balance be with U"(愿平衡与你同在) 视窗介面游戏设计教学,讨论,分享。欢迎来信。 视窗程式设计(Windows CLR Form)游戏架构设计(Game Application Framework) 游戏工具设计(Game App. Tool Design ) 电脑图学架构及研究(Computer Graphics) --



※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 118.167.138.65 ※ 编辑: NDark 来自: 118.167.138.65 (08/31 22:16)
1F:推 newcinka:推 08/31 22:26
※ 编辑: NDark 来自: 118.167.138.65 (08/31 23:18)
2F:→ exe44:之前也想尝试类似的架构, 但是在介面存在与否的问题上总是 09/01 00:27
3F:→ exe44:会有点不顺利的感觉(C++), 或许有些设计机制可以达成, 可是 09/01 00:29
4F:→ exe44:又觉得太过罗嗦? 某些动态语言似乎比较没这方面问题? 09/01 00:30
5F:推 imagefish:好文! 09/01 02:22
6F:推 linjack:推荐这篇文章 09/01 03:05
7F:推 Kendai:推推 09/01 06:01
8F:推 rexrainbow:推! 09/01 13:53
9F:推 ddavid:推 09/01 19:25
10F:推 nobody1:推 09/01 22:20
11F:→ NDark:我的经验是,会好好用继承,设计模式的程式员都很少见了 09/01 22:50
12F:→ NDark:若是用不成熟的态度来实作元件式架构,只会更是一团乱而已. 09/01 22:50
13F:推 Qshi:翻得好顺好读! 感恩 09/08 17:15
14F:推 HalfLucifer:推荐这篇文章!包括Ref那些文章都是极佳的架构好文! 09/10 11:19







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:iOS站内搜寻

TOP