电子游戏如何面向对象?[关闭]


18

我一直想知道电子​​游戏,尤其是大型3D项目在多大程度上使用了面向对象的技术。我认为这两者都是很酷的,troll并且werewolfenemy其中的某些属性继承并重写了它们的属性。但是,也许课程太重而无法进行实践,我不知道...


2
如何量化(甚至客观地限定)面向对象?您要在迈耶斯(Meyers)还是达尔·尼加德(Dahl-Nygaards)中回答?

显然,它们是如此沉重,以至于基于OO的语言是行业标准。
共产党鸭子

4
如果您是说C ++,那么选择它是因为它是一种以注重速度的方式探索类型安全的泛型编程的好语言。少数OOP功能显然是不幸的设计错误,仅用于向后兼容。;)

Answers:


20

举一个例子,我将解释我对电子游戏中实体的想法。我不会讨论对象和类的一般优缺点,也不会讨论它们在电子游戏中的全部作用。

小型电子游戏中的实体通常由单独的类定义,在大型游戏中,它们通常在文件中定义。有两种通用方法可以遵循:

扩展:在您的示例中TrollWerewolf将扩展Enemy,即扩展EntityEntity会照顾运动,健康等基本事物,同时Enemy将子类声明为敌人并做其他事情(Minecraft会遵循这种方法)。

基于组件:第二种方法(也是我最喜欢的方法)是一Entity类,其中包含组件。组件可以是MoveableEnemy。这些组件只关心运动或物理等件事。此方法会中断较长的扩展链,并最大程度地降低发生冲突的风险。例如:如果不可移动敌人是从可移动角色继承而来的,该如何使它们成为敌人?

在《魔兽世界》或《星际争霸2》等大型电子游戏中,实体不是类别,而是指定整个实体的数据集。脚本编制也很重要,因为您可以定义实体不是在类中而是在脚本中定义的(如果它是唯一的,则不是移动之类的东西)。

我目前正在为RTS编程,我采用基于组件的方法来处理实体,技能,物品以及游戏中所有动态的描述符和脚本文件。


2
听起来很有趣。但是我不会说谎,我不知道component在这种情况下a 是什么。链接将不胜感激。
vemv 2011年

组件是负责单一功能的实体。基于组件的实体将拥有一个组件(如果是这种情况),它将不会从该组件继承。所有权意味着实体可以停用其组成部分之一以更改其行为。在经典扩展范例中,行为是由于“属于”超类。实体可以是组件,并且可以根据需要将组件建模为类或结构。
FxIII 2011年

组件(有时称为实体系统)-您可以在此博客中搜索想法t-machine.org 有变体,但基本思想是,组件是一个特殊目的的对象,您的参与者将一堆组件捆绑在一起,例如构建模型。来自乐高。
Patrick Hughes

2
我开了一个有关游戏开发的博客,并写了我的第一个博客文章。没什么新鲜的,但是有一点代码:jagamedev.wordpress.com/2011/06/26/…–
Marco Marco

23

我认为巨魔和狼人都从敌人那里继承了它的属性并改写了其中的一些,这是很酷的。

不幸的是,这种在90年代流行的多态方法在实践中已证明是个坏主意。假设您将“狼”添加到敌人列表中-好吧,它与狼人共享一些属性,因此您希望将这些属性合并到一个共享的基类中。'WolfLike'。现在,您将“人类”添加到敌人列表中,但是狼人有时是人类,因此它们也共享一些属性,例如两条腿走路,能够说话等。您是否为人类和狼人创建了“类人动物”共同基础,然后您是否必须停止从WolfLike派生的狼人?还是您要同时继承两者?在这种情况下,当类人动物中的某物与WolfLike中的某物发生冲突时,哪个属性优先?

问题在于尝试将现实世界建模为类树是无效的。采取任何3件事,您可能会发现4种不同的方式将其行为分解为共享和非共享的因素。这就是为什么许多人转而使用模块化或基于组件的模型的原因,在该模型中,一个对象由几个较小的对象组成,这些较小的对象组成整体,并且可以交换或更改较小的对象以组成所需的行为。在这里,您可能只有1个敌人类别,并且包含有关其行走方式的子对象或组件(例如Bipedal与Quadpededal),攻击方法(例如咬伤,爪子,武器,拳头),皮肤(绿色)巨魔,毛茸茸的狼人和狼),等等。

这仍然是完全面向对象的,但是什么是有用的对象的概念与课本中通常讲授的对象不同。通常,您只有一个表示一个抽象概念的具体类(例如“敌人”),而不是拥有一个包含各种抽象类的大型继承树和几个具体类的树,而是包含了更多表示更多抽象概念的具体类。 (例如,攻击,皮肤类型)。有时,可以通过1级继承来最好地实现这些第二类(例如,基本的Attack类和针对特定攻击的几个派生类),但是深层继承树就消失了。

但是,也许课程太重而无法进行实践,我不知道...

在大多数现代语言中,课程并不是“繁重的”。它们只是编写代码的另一种方式,它们通常只是包装您无论如何都要编写的过程方法,除了语法更简单之外。但是,无论如何,不​​要编写函数可以使用的类。类在本质上并不是更好,只有在它们使代码更易于管理或理解的地方。


3
喜欢这个答案:)
Czarek Tomczak

8

我不确定您所说的“太重了”是什么意思,但是C ++ / OOP是游戏开发的通用语言,其理由与在其他地方使用的相同。

谈论高速缓存是一个设计问题,而不是语言功能。由于C ++在过去15到20年中一直在游戏中使用,因此它可能还不错。Java可以算是很不错的,因为它已在智能手机的非常紧凑的运行时环境中使用。

现在到一个真正的问题:多年来,阶级等级制度变得越来越深,并开始遭受与此有关的问题。在最近一段时间,类层次结构已经变平,组成和其他数据驱动的设计正在被继承,而不是逻辑驱动的继承。

这些都是面向对象的,并且在设计新软件时仍用作基准,只是对类所体现的关注有所不同。


2

类不涉及任何运行时开销。运行时多态仅通过函数指针进行调用。与其他开销相比,这些开销是极少的,例如Draw调用,并发同步,磁盘I / O,内核模式切换,较差的内存位置,过度使用动态内存分配,较差的算法选择,使用脚本语言和列表继续。面向对象本质上已经取代了之前的东西,这是有原因的,这是因为它要好得多。


在各种多态类型之间进行分配的方式会导致较差的内存局部性。即使在最轻量级的实现(例如C ++ vtables或Objective-C IMP缓存)中,如果您实际上使用多态来处理各种类型的对象,也会破坏缓存。当然,如果您不使用多态性而不是vtable保留在缓存中,或者IMP缓存的消息导致正确的位置,那么为什么要麻烦呢?在Java或C#中,成本更高,而在JavaScript或Python中,成本仍然更高。

1
@Joe Wreschnig:如果您使用的是那些较慢的语言,则与其他成本(如解释/ JIT)相比,OO的成本微不足道。更重要的是,您可以对所需的内存局部性进行理论上的设计,但事实是,分支预测,乱序执行以及CPU上的类似功能几乎完全抵消了成本。否则,为什么使用面向对象编写了这么多高性能软件?
DeadMG 2011年

1

要意识到的一件事是,面向对象代码的概念通常比其在语言中的实现更有价值。特别是,必须在主循环中的实体上执行数千次虚拟更新调用,这可能会在360或PS3等平台上的C ++中的缓存中造成混乱-因此,该实现可能会避免使用“虚拟”或“类”关键字的速度。

尽管它通常仍会遵循面向对象的继承和封装概念-只是以与语言本身不同的方式来实现它们(例如,好奇地递归模板,合成而不是继承(Marco描述的基于组件的方法))。如果继承总是可以在编译时解决,那么性能问题就消失了,但是代码可能会因模板,自定义RTTI实现(同样内置的实现通常不切实际地缓慢)或在宏等

在iOS / Mac上的Objective-C中,消息传递方案(甚至是高级缓存)也存在类似但更严重的问题...


0

我将尝试使用的方法将类继承和组件混合在一起。(首先,我不使Enemy成为一个类,而是使它成为一个组件。)我不喜欢这样的想法:对所有内容使用一个通用Entity类,然后添加必要的组件以充实该实体。相反,我更喜欢让一个类在构造函数中自动添加组件(和默认值)。然后,子类在其构造函数中添加其其他组件,因此子类将具有其组件和其父类组件。例如,武器类将添加所有武器通用的基本组件,然后剑子类将添加特定于剑的任何其他组件。我仍在争论是否使用text / xml资产来定义实体的值(例如特定的剑),或在代码中完成所有操作,或正确的组合方式。这仍然允许游戏使用代码语言的类型信息和多态性,并使ObjectFactories更加简单。


3
嘿克里斯。分享您的经验和计划固然很棒,但实际上并不能直接回答问题。我认为问题更多是关于这些事情通常如何完成的,而您正在回答如何计划这样做。他们是类似的问题,但并非完全相同。
MichaelHouse
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.