Bright Night Games

Games


Download | Screenshots | Development



Whiplash Development Info
Written by Paul
September 13, 2008

Forward

I wrote this article about how we specifically programmed Whiplash, a 2D sophomore game project. Unfortunately, over the past few months my knowledge about game programming has vastly improved leaving most of this information out dated. Nevertheless, I feel it still demonstrates an understanding of adolescent game development.
Enjoy!

The Engine

When people talk about game engines and what they do, they’re referring to the backbone of any game. The engine is what drives the game by efficiently performing various tasks in short amounts of time. The core responsibilities of the game engine are gathering input from the player, handling AI, updating physics data, playing sounds, and rendering objects to the screen. Of course there are many different types of games and engines, but for the most part, they are all responsible for at least those core tasks. Many engines also incorporate some kind of object management and file IO system, but more on that later.

Another key point to keep in mind is that the game engine is usually comprised of many sub-engines (or managers). Examples:

  Graphics Manager – render objects, textures, models
  Physics Manager – collisions, apply forces
  AI Manager – pathfinding, agent behavior
  Sound Manager – play sounds and effects
  Game Logic Manager – game specific code
  Input Manager – gather input (mouse, keyboard, controller)

These sub-engines shouldn’t need to know anything about each other. For instance, the physics manager doesn’t need to know how objects are rendered in the game, and the sound manager shouldn’t care about how the input is taken. That is one of the biggest advantages of using a system like this. Basically, you could have a different programmer programming each sub-engine of the same game without having to know anything about how the other sub-engines work. However, this brings up a good point. How do all of these seemingly isolated managers work together to create a game? Well, that’s one of the responsibilities of the game engine. It allows all of these sub-engines to operate on the same data, which brings me to the next topic.

Game Objects

There are many different ways to store data, but the way I’ve found most effective is using a component-based system. This basically means that all enemies, obstacles (walls, platforms, and level geometry), power-ups, weapons, etc are game objects that are kept on one big list. The definition of a game object might look like this:

  class gameObject 
  {
    GraphicsComponent *pGfxCmpnt;
    PhysicsComponent *pPhyCmpnt;
    SharedComponent *pShrdCmpnt;
  };

A component is comprised of information (the data), and each game object has a pointer to a different component. For example, the graphics component might store texture and animation information while the physics component might have collision information. The shared component is used for data that would be duplicated. For instance, graphics needs to know where to draw each object, but so does physics, so instead of having two copies of position, the position of each object is kept under the shared component. Keep in mind the components are simply the data associated with each game object, and each game object has its own unique set of information.

With this data structure, each manager will loop through this list of game objects modifying data. The advantage to this system is that for objects that don’t necessarily need graphics data (like invisible objects) their graphics component pointer can be NULL. Then later when the graphics sub-engine is called to update, the object will be skipped altogether because it doesn’t need to be rendered. This approach also works for objects that don’t need to be checked for collision.

For some games that tend to have a lot of object or levels with lots of objects, this approach may be too slow. Each sub-engine might have to loop through thousands of objects per frame, and this could bog the engine down. We don’t want to be looping though all of those objects every frame, but maybe we don’t have to. If we only need to update the objects on the screen (since this is all the player can actually see), we can create a smaller list of pointers that point into our master list of game objects. This smaller list will need to be updated every game loop, but now each sub-engine will loop a much smaller amount of objects.

The Game Loop / Object Pipeline

Now that we have an idea about how object data is stored, we need to know exactly how the sub-engines should be called to update the game objects. This is handled in the main game loop. The main game loop is constantly being executed and updating sub-engines until the user decides to quit the game. Ideally, the main game loop should remain fairly. Here is an example:

  While (game.pGameMgr.quit == false)
  {
    game.pInputMgr.Update();
    game.pAIMgr.Update();
    game.pPhysicsMgr.Update();
    game.pGfxMgr.Update();
  }

One key idea to note here is that each manager is called to update on ALL objects before the next manager is called. So collision has been checked on every object before the graphics manager updates. The reason being that if one frame (a frame consists of one complete game loop cycle) an object has moved on top of another object when it shouldn’t have, the physics manager can update the remaining objects instead of the graphics manager drawing the overlapping objects incorrectly. Also, since drawing to the screen tends to be very slow, it is much better to only do this once per frame.

File IO System

When dealing with game specific data like levels, enemies, the player, or other similar things, it is generally a good idea to load this information from a file. For example, it would be an extremely painful process if you were creating a 2D tile-based game by hard coding the levels. You would have to recompile every time you made a change, and you would be wasting tons of time coding content when a simple level file system would have worked much better. Usually file systems are closely related to editors since the editor will be exporting information to a file that your game engine will use.
I’ll take the example of a 2D tile-based game. The data for a simple level could be stored like this:

  WEEEEEEEEEEEEW
  WEEEEGGGGEEEEW
  WEEEEEEEEEEEEW
  GGGGGGGGGGGGGG

Where each ‘W’ represents a wall tile, each ‘E’ represents an empty tile and each ‘G’ represents a ground tile. One big advantage is that the file sizes will remain small, but there are some drawbacks. What if you wanted to have two tiles on the same location like a fish spawning on a water tile? Also there’s a possibility that a lot of space could be wasted on particularly long or tall levels with lots of empty tiles. Another way to store level data could be done by storing the x and y position of each tile with a unique ID.

  x, y, ID

Where the first two numbers are the position of the tile and the third corresponds to the type like wall, ground, or fish. Now when the engine is parsing the level file it knows that every wall tile will have ID 1 and each fish has ID 2. This system offers the most flexibility since tiles may overlap and adding new tile types is relatively easy. The biggest drawback is that file sizes can increase since we’re being a bit more verbose.
Additionally, it is nice to have some sort of configuration file that gets loaded during initialization. Some data you might want to load:

  - Screen Width / Height
  - Fullscreen (true / false)
  - Resolution (low / medium / high)

The ability to change options like this without compiling can be a huge benefit when testing. So when you need to test your game on multiple monitors with different resolutions it’s very easy to see what works, what doesn’t, and if any slight modifications will fix your problems.

Slide