以OO方式进行游戏设计

发布于 2024-08-21 15:39:38 字数 822 浏览 6 评论 0原文

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

do {
  for (GameEntity entity : entities) {
    entity.update(gameContext);
  }

  for (Drawable drawable : drawables) {
    drawable.draw(graphics2d);
  }
} while (gameRunning);

当一个实体被指示更新自身时,它将根据当前施加到它的力来调整其速度和位置。但是,我需要实体表现出其他行为;例如,如果“坏人”被玩家射杀,则该实体应该被摧毁并从游戏世界中删除。

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

以下是我到目前为止想到的选项:

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

I'm designing a simple game, which uses Java 2D and Newtonian physics. Currently my main "game loop" looks something like:

do {
  for (GameEntity entity : entities) {
    entity.update(gameContext);
  }

  for (Drawable drawable : drawables) {
    drawable.draw(graphics2d);
  }
} while (gameRunning);

When an entity is instructed to update itself it will adjust its velocity and position based on the current forces applied to it. However, I need entities to exhibit other behaviour; e.g. if a "bad guy" is shot by a player the entity should be destroyed and removed from the game world.

My question: What is the best way to achieve this in an object oriented manner? All examples I've seen so far incorporate the game loop into a God class called something like Game, which performs the steps: detect collisions, check-if-bad-guy-killed, check-if-player-killed, repaint, etc and encapsulates all the game state (lives remaining, etc). In other words, it's very procedural and all the logic is in the Game class. Can anyone recommend a better approach?

Here are the options I've thought of so far:

  • Pass a GameContext to each entity from which the entity can remove itself if required or update the game state (e.g. to "not running" if the player is killed).
  • Register each GameEntity as a listener to the central Game class and take an event oriented approach; e.g. a collision would result in a CollisionEvent being fired to the two participants in the collision.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

我的影子我的梦 2024-08-28 15:39:38

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

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

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

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

以下是这种方法的优点:

  • 您可以自由组合功能
    无需为每个人编写新课程
    组合或使用复杂的继承
    树。

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

  • ID 到组件的查找可能看起来像
    贵,但是服务还行
    大部分的密集工作
    迭代所有组件
    属于特定类型。在这些情况下,
    存储所有数据效果更好
    您需要一个整洁的列表。

I have worked closely with two commercial game engines and they follow a similar pattern:

  • Objects represent components or aspects of a game entity (like physical, renderable, whatever), rather than the whole entity. For each type of component there is a giant list of components, one for each entity instance that has the component.

  • The 'game entity' type itself is just a unique ID. Each giant list of components has a map to look up the component (if any exists) that corresponds to an entity ID.

  • If a component requires an update it is called by a service or system object. Each service is updated directly from the game loop. Alternatively, you could call services from a scheduler object which determines update order from a dependency graph.

Here are the advantages of this approach:

  • You can freely combine functionality
    without writing new classes for every
    combination or using complex inheritance
    trees.

  • There is almost no functionality that
    you can assume about all game
    entities that you could put in a game
    entity base class (what does a light
    have in common with a race car or a
    sky-box?)

  • The ID-to-component look-ups might seem
    expensive, but services are doing
    most of the intensive work by
    iterating through all the components
    of a particular type. In these cases,
    it works better to store all the data
    you need in a single tidy list.

初心未许 2024-08-28 15:39:38

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

我们还在一个可以动态更改的单独对象中完成了实际的物理建模。这让我们很容易搞乱重力等。

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

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

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

In one particular engine that I worked on, we decoupled the logic from the graphic representation and then had objects that would send messages for what they wanted to do. We did this so that we could have games exist on a local machine or networked and they were indistinguishable from each other from a code standpoint. (Command pattern)

We also had the actual physics modeling done in a separate object that could be changed on the fly. This let us easily mess with gravity, etc.

We made heavy use of the event driven code (listener pattern), and lots and lots of timers.

For example, we had a base class for an object that was intersectable that could listened to the collision event. We subclassed it into a health box. On collision, if it was hit by a player entity, it sent a Command to the collider that it should gain health, sent a message to broadcast a sound to all that could hear it, deactivated collisions, activated the animation to remove the graphics from the scene graph, and set a timer to reinstantiate itself later. It sounds complicated, but it really wasn't.

If I recall (and it's been 12 years), we had the abstract notion of scenes, so a game was a sequence of scenes. When a scene was finished, an event was fired that would typically send a command take down the current scene and start another one.

私野 2024-08-28 15:39:38

我不同意这一点,因为你有一个主 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 和 Bubble 之间的碰撞相关的逻辑Needle 绝对不在主 Game 类中。

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

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

这是不正确的。

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

I disagree that because you have a main Game class all the logic has to happen in that class.

Over-simplification here mimicking your example just to make my point:

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()

Now you have a Bubble class:

Bubble implements Drawable {

handle( Needle needle ) {
    if ( needle collide with us ) {
        exploded = true;
    }
}

draw (...) {
   if (!exploded) {
      draw();
     }
  }
}

So, sure, there's a main loop that takes care of passing the messages around between the entities but the logic related to the collision between a Bubble and a Needle is definitely not in the main Game class.

I'm pretty sure that even in your case all the logic related to the movement is not happening in the main class.

So I disagree with your statement, that you written in bold, that "all the logic happens in the main class".

This is simply not correct.

As for good design: if you can easily provide another "view" of your game (like, say, a mini-map) and if you can easily code a "frame-for-frame perfect replayer", then your design is probably not that bad (that is: by recording only the inputs and the time at which they happened, you should be able to recreate the game exactly as it was played. That's how Age of Empires, Warcraft 3, etc. make their replay: it's only the user inputs and the time at which they happened that is recorded [that's also why the replay files are typically so tiny]).

说好的呢 2024-08-28 15:39:38

我编写自己的引擎(原始的和肮脏的),但是具有良好的 OO 模型的预构建引擎是 Ogre 。我建议看一下它(它是对象模型/API)。节点分配有点古怪,但你越看越觉得它很有意义。它还通过大量有效的游戏示例进行了非常详细的记录。

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

I write my own engines (raw & dirty), but a pre-built engine that has a decent OO model, is Ogre. I'd recommend taking a look at it (it's object model / API). The node assignment is a tad funky, but it makes perfect sense the more you look at it. It's also extremely well documented with a ton of working game examples.

I've learned a couple tricks from it myself.

清秋悲枫 2024-08-28 15:39:38

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

作为埃文·罗杰斯出色答案的附录,您可能对这些文章感兴趣:

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

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

I'm only posting this as an answer because I'm not able to comment on other posts yet.

As an addendum to Evan Rogers excellent answer you might be interested in these articles:

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

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

少女净妖师 2024-08-28 15:39:38

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

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

This game was an experiment in keeping the model and view separate. It uses the observer pattern to notify the view(s) of changes in the game's state, but events would perhaps offer a richer context. Originally, the model was driven by keyboard input, but the separation made it easy to add timer-driven animation.

Addendum: You need to keep the game's model separate, but you can re-factor that model into as many classes as required.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文