我正在设计一款简单的游戏,它使用Java 2D和牛顿物理。目前我的主要“游戏循环”看起来像:
do {
for (GameEntity entity : entities) {
entity.update(gameContext);
}
for (Drawable drawable : drawables) {
drawable.draw(graphics2d);
}
} while (gameRunning);
当指示实体自我更新时,它将根据施加于其上的当前力调整其速度和位置。但是,我需要实体展示其他行为;例如如果玩家射杀了“坏人”,那么该实体应该被摧毁并从游戏世界中移除。
我的问题:以面向对象的方式实现这一目标的最佳方法是什么?到目前为止,我见过的所有例子都将游戏循环合并到一个名为Game
的神类中,它执行以下步骤:检测碰撞,检查是否坏人杀死,检查是否 - 玩家杀死,重新粉刷等,并封装所有游戏状态(生命剩余等)。换句话说,它非常程序,所有逻辑都在Game类中。任何人都可以推荐更好的方法吗?
以下是我到目前为止所考虑的选项:
GameContext
传递给实体可以从中移除的每个实体,或者更新游戏状态(例如,如果玩家被杀,则为“未运行”)。GameEntity
注册为中心Game
类的听众,并采用面向事件的方法;例如碰撞会导致CollisionEvent
被发射给碰撞中的两个参与者。 答案 0 :(得分:14)
我与两个商业游戏引擎密切合作,他们遵循类似的模式:
对象代表游戏实体的组件或方面(如物理,可渲染,等等),而不是整个实体。对于每种类型的组件,都有一个巨大的组件列表,每个组件都有一个组件。
“游戏实体”类型本身只是一个唯一的ID。每个巨大的组件列表都有一个映射,用于查找与实体ID对应的组件(如果存在)。
如果组件需要更新,则由服务或系统对象调用。每个服务都直接从游戏循环更新。或者,您可以从调度程序对象调用服务,该对象从依赖关系图确定更新顺序。
这种方法的优点如下:
您可以自由组合功能 没有为每个人写新课程 组合或使用复杂的继承 树。
几乎没有任何功能 你可以假设所有游戏 你可以放在游戏中的实体 实体基类(什么是光 与赛车或赛车有共同之处 天空盒?)
可能会出现ID到组件的查找 贵,但服务正在做 大部分的密集工作 迭代所有组件 一种特殊的类型。在这些情况下, 它可以更好地存储所有数据 你需要一个整洁的清单。
答案 1 :(得分:6)
在我工作的一个特定引擎中,我们将逻辑与图形表示分离,然后有对象发送消息以实现他们想要做的事情。我们这样做是为了让我们可以在本地机器或网络上存在游戏,从代码的角度来看它们彼此无法区分。 (命令模式)
我们还在一个可以即时更改的单独对象中完成了实际的物理建模。这让我们很容易陷入引力等等。
我们大量使用事件驱动代码(监听器模式)和大量定时器。
例如,我们有一个可交叉的对象的基类,可以监听碰撞事件。我们将其分为健康箱。在碰撞时,如果它被玩家实体击中,它会向碰撞器发送一个命令,它应该获得健康,发送消息向所有可以听到它的人广播声音,停用碰撞,激活动画以从中删除图形场景图,并设置一个计时器以便以后重新实例化。这听起来很复杂,但事实并非如此。
如果我记得(已经12年了),我们就有了抽象的场景概念,所以游戏就是一系列场景。当场景结束时,会触发一个事件,通常会发送一个命令来取消当前场景并启动另一个场景。
答案 2 :(得分:3)
我不同意,因为你有一个主要的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 implements Drawable {
handle( Needle needle ) {
if ( needle collide with us ) {
exploded = true;
}
}
draw (...) {
if (!exploded) {
draw();
}
}
}
所以,当然,有一个主循环负责传递实体之间的消息,但与Bubble和Needle之间的碰撞相关的逻辑肯定不在主Game类中。
我很确定即使在你的情况下,所有与运动相关的逻辑都不会发生在主类中。
所以我不同意你的陈述,你用粗体写的,“所有的逻辑都发生在主要的课堂上。”
这根本不正确。
至于好的设计:如果你可以轻松提供游戏的另一个“视图”(例如,迷你地图),如果你可以轻松地编写“逐帧完美的重播”,那么你的设计可能并不那么糟糕(也就是说:通过仅记录输入和发生的时间,您应该能够完全按照比赛的方式重新创建游戏。这就是帝国时代,魔兽争霸3等的重播方式:它只是用户输入和记录它们发生的时间[这也是为什么重放文件通常很小的原因]。
答案 3 :(得分:2)
我编写自己的引擎(原始和肮脏),但是具有体面OO模型的预构建引擎是Ogre。我建议看看它(它的对象模型/ API)。节点分配有点时髦,但是你看的越多就越有意义。大量的工作游戏示例也证明了这一点。
我自己从中学到了一些技巧。
答案 4 :(得分:2)
我只是将此作为答案发布,因为我还无法对其他帖子发表评论。
作为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/
答案 5 :(得分:1)
此game是一项将模型和视图分开的实验。它使用观察者模式来通知视图中游戏状态的变化,但事件可能会提供更丰富的上下文。最初,该模型是由键盘输入驱动的,但分离使得添加定时器驱动的动画变得容易。
附录:您需要将游戏模型分开,但您可以根据需要将该模型重新分解为多个类。