以OO方式进行游戏设计

我正在设计一个简单的游戏,它使用Java 2D和牛顿物理。 目前我的主要“游戏循环”看起来像:

do { for (GameEntity entity : entities) { entity.update(gameContext); } for (Drawable drawable : drawables) { drawable.draw(graphics2d); } } while (gameRunning); 

当指示实体自我更新时,它将根据施加于其上的当前力调整其速度和位置。 但是,我需要实体展示其他行为; 例如,如果玩家射击了“坏人”,则该实体应该被销毁并从游戏世界中移除。

我的问题 :以面向对象的方式实现这一目标的最佳方法是什么? 到目前为止,我所见过的所有例子都将游戏循环合并到一个名为Game的God类中,它执行以下步骤: 检测碰撞,检查是否坏人杀死,检查是否被玩家杀死,重新绘制等等并封装所有游戏状态(剩余生命等)。 换句话说,它是非常程序化的所有逻辑都在Game类中 。 有人能推荐更好的方法吗?

以下是我到目前为止所考虑的选项:

  • GameContext传递给每个实体,如果需要,实体可以从中移除自身或更新游戏状态(例如,如果玩家被杀,则“不运行”)。
  • 将每个GameEntity注册为中心Game类的监听器,并采用面向事件的方法; 例如,碰撞会导致CollisionEvent被发射到CollisionEvent中的两个参与者。

我与两个商业游戏引擎密切合作,他们遵循类似的模式:

  • 对象表示游戏实体的组件或方面(如物理,可渲染,等等),而不是整个实体。 对于每种类型的组件,都有一个巨大的组件列表,每个组件对应一个组件。

  • “游戏实体”类型本身只是一个唯一的ID。 每个巨大的组件列表都有一个映射,用于查找与实体ID对应的组件(如果存在)。

  • 如果组件需要更新,则由服务或系统对象调用。 每个服务都直接从游戏循环更新。 或者,您可以从调度程序对象调用服务,该对象从依赖关系图确定更新顺序。

以下是此方法的优点:

  • 您可以自由组合function,而无需为每个组合编写新类或使用复杂的inheritance树。

  • 几乎没有任何function可以假设您可以放入游戏实体基类中的所有游戏实体(灯光与赛车或天空盒有什么共同之处?)

  • ID到组件的查找可能看起来很昂贵,但是服务通过迭代特定类型的所有组件来完成大部分密集工作。 在这些情况下,最好将所需的所有数据存储在一个整洁的列表中。

在我工作的一个特定引擎中,我们将逻辑与图形表示分离,然后有对象发送消息以实现他们想要做的事情。 我们这样做是为了让我们可以将游戏存在于本地计算机上或联网,并且从代码角度来看它们彼此无法区分。 (命令模式)

我们还在一个单独的对象中完成了实际的物理建模,可以在运行中进行更改。 这让我们很容易陷入重力等问题。

我们大量使用事件驱动代码(监听器模式)和大量定时器。

例如,我们有一个可交叉的对象的基类,可以监听碰撞事件。 我们将其分为健康箱。 在碰撞时,如果它被玩家实体击中,它会向对撞机发送一个命令,它应该获得健康,发送消息向所有可以听到它的人广播声音,停用碰撞,激活动画以从中删除图形场景图,并设置一个计时器以便以后重新实例化。 这听起来很复杂,但事实并非如此。

如果我记得(已经12年了),我们就有了抽象的场景概念,所以游戏就是一系列场景。 当场景结束时,会触发一个事件,该事件通常会发送一个命令,取消当前场景并启动另一个场景。

我不同意,因为你有一个主要的Game类,所有的逻辑都必须在那个类中发生。

过度简化这里模仿你的例子只是为了说明我的观点:

 mainloop: moveEntities() resolveCollisions() [objects may "disappear"/explode here] drawEntities() [drawing before or after cleanEntitites() ain't an issue, a dead entity won't draw itself] cleanDeadEntities() 

现在你有一个Bubble类:

 Bubble implements Drawable { handle( Needle needle ) { if ( needle collide with us ) { exploded = true; } } draw (...) { if (!exploded) { draw(); } } } 

所以,当然,有一个主循环负责传递实体之间的消息,但与Bubble和Needle之间的冲突相关的逻辑肯定不在主Game类中。

我很确定即使在你的情况下,所有与运动相关的逻辑都不会发生在主类中。

所以我不同意你的陈述,你用粗体写的,“所有逻辑都发生在主类中”。

这根本不正确。

至于好的设计:如果你可以轻松提供你游戏的另一个“视图”(比如说,迷你地图),如果你可以轻松地编写“逐帧完美的重播”,那么你的设计可能不是这很糟糕(也就是说:只记录输入和发生的时间,你应该能够完全按照比赛的方式重新创建游戏。这就是帝国时代,魔兽争霸3等等他们的重播:它只是用户输入和记录的时间[这也是重播文件通常如此微小的原因])。

我编写了自己的引擎(原始和脏),但是具有合适的OO模型的预构建引擎是Ogre 。 我建议看看它(它的对象模型/ API)。 节点分配有点时髦,但是你看的越多就越有意义。 大量的工作游戏示例也记录得非常好。

我自己从中学到了一些技巧。

我只是将此作为答案发布,因为我还无法对其他post发表评论。

作为Evan Rogers的补充答案,您可能会对这些文章感兴趣:

http://www.gamasutra.com/blogs/MeganFox/20101208/6590/Game_Engines_101_The_EntityComponent_Model.php

http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/

这个游戏是一个保持模型和视图分离的实验。 它使用观察者模式来通知视图中游戏状态的变化,但事件可能会提供更丰富的上下文。 最初,该模型由键盘输入驱动,但分离使得添加定时器驱动的动画变得容易。

附录:您需要将游戏模型分开,但您可以根据需要将该模型重新分解为多个类。