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:
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).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. 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.