We are a 3 person team. Here are our games so far:

Author Archive

Retro Game Internals: Contra Levels

Sunday, March 16th, 2014

Tile Map

Each level in the game is made up of a series of independent screens. The image below shows an overview of the first level of the game divided up into the 13 screens that comprise the stage.

screens

Each screen is built from a collection of non-overlapping super tiles which each take up 32×32 pixels worth of screen space. A screen is always made of exactly 8 super tiles across and 7 from top to bottom. This results in a final screen size of 256×224 pixels. The native resolution of the NES is 256×240 pixels so Contra leaves some of the screen space unused rather than trying to deal with splitting super tiles in half to perfectly fill up the display. It was fairly common at the time for console games to not care what they were displaying at the very top and bottom of the screen because it often wasn’t visible on CRT TVs.

The image below shows the 7th screen of the first level of Contra divided up into the 56 super tiles that make up the background.

supertiles

Each super tile is in turn made up of 4×4 regular tiles which are 8×8 pixels each in size. These regular tiles are what the NES hardware actually uses to display background maps so all games need to break their maps down into 8×8 pixel tiles at some point.

Collision Data

The collision information for each screen is derived from the final layout of the regular tiles as opposed to being explicitly authored as a separate map. The resulting collision map is made up of 16×16 pixel blocks of the screen. This means that there are 2×2 regular tiles that make up each block of collision data, but when deriving the collision type for each block, only the upper left regular tile is considered.

Each level defines 4 ranges of tile indices with all of the tiles in each range having 1 of the 4 different collision types. For example in the first level, tiles 0x01 to 0x05 have the collision type for floors that you can land on when falling but pass through when moving left, up and right. Tiles 0x06 to 0xF8 have the empty space collision type. Tiles 0xF9 to 0xFE have the water collision type and tile 0xFF has the completely solid collision type.

The image below shows a part of the first level of the game with the resulting collision map overlaid. Blocks with collision type water are colored blue and blocks with collision that allows you to walk on them but also move through them are colored green. Empty space collision blocks are black and completely solid collision blocks are not used in this section of the level.

collision

Enemies

Each screen has a list of the enemies that need to be spawned into the world as you scroll through it. The list is presorted according to the distance you need to have scrolled the screen in order to spawn the enemy. For example, the first enemy in the first level has a scroll value of 16, so once you start playing and move far enough to the right to scroll the screen 16 pixels, that first enemy is spawned. Because the list is sorted by scroll distance and the screen can only scroll in 1 direction, there is always exactly 1 candidate for the next enemy to be spawned.

The second piece of data in each list entry is the type of enemy to create, along with a count of how many of those enemies to spawn. Then, for each of those count of enemies, there is a generic creation context value and a value that gives the position to spawn the enemy on screen. The creation context has no meaning to the spawning system itself, but will be interpreted later by the enemy’s own update logic. It is used, for example, to configure which kind of power-up a flying power-up balloon will drop, and to control certain aspects of the behavior of the running soldiers. The spawn position value gives only half of the information required to locate the new enemy on screen. For levels that scroll horizontally, the position value will give the y position of the enemy, and the x position will always be the far right side of the screen. If the level scrolls vertically then the spawn position value is the x position of the enemy and the y position is always at the top of the screen. Enemies that seem to spawn from the trailing edge of the screen have logic built in to their enemy specific update logic to warp themselves to the opposite side of the screen as their first action.

The variable length enemies list is terminated by a byte with a magic value and then the game will wait for you to scroll onto the next screen to begin looking for more enemies to spawn from that screen’s enemies list. Enemies are in charge of destroying themselves when they have scrolled off of the screen, there is no system that does it automatically.

(Prev – This is part 2 of a 7 part series – Next)

Retro Game Internals

Monday, March 10th, 2014

Like a lot of people doing programming for video games, I played a lot of NES games when I was a kid. It has always amazed me how they were able to get so much out of so little, and so I’ve spent a good amount of time over the years dissecting certain games to see how they work. Today I’m starting a series of blog posts in which I want to document what I’ve learned, from a game programmer’s point of view. I’m going to try to focus on how the game systems work at the game/engine level and not at the hardware level (so more like how does this game decide which things it wants to draw on this frame as opposed to how do sprites work on the NES.) I’ll also try to throw in any tidbits about the games that I think are interesting like things that weren’t obvious to me just from playing the game casually or instances of bugs in the game’s logic.

Contra Introduction

The first game that I’m going to write about is Contra on the NES. The remainder of this post will be a short overview of the objects and data that exist in a game of Contra and then subsequent posts will go into more detail about each system the game uses to manipulate its model of the world. The basic model that the game maintains is made up of:

  • Player characters
  • Player bullets
  • Enemies and other objects
  • Level data

The player characters are the main heavyweight objects of the game and have lots and lots of code that explicitly deals with them as you might expect. The bullets from player characters are treated differently from every other kind of object in the game for reasons that are probably performance related. We’ll see later on how the player bullets being separated out from the other game objects makes various things the game has to do more efficient. The final class of objects are the enemies which are managed by a simple entity system. This includes the enemies themselves along with enemy bullets and explosions, but also a couple of friendly things like flying power-up balloons and power-ups sitting on the ground. I’ll use the term enemies to refer to all of these things just to have a more descriptive term for them than “objects.” The key feature is that there is code in the game that deals with them abstractly as opposed to the player characters and player bullets which are always manipulated by specialized code.

The tile based level data is the final major part of the game simulation. Contra features standard horizontal levels along with a vertical level and pseudo-3D levels. Levels only support moving forward through them, never backwards. The game maintains a double buffer of tiles that hold collision and visual data for the current screen in the first buffer, while it builds the next screen in the second buffer. We’ll see later on how the level data is organized, how it is updated as you make your way through the levels and how enemies are spawned from it.

Up Next

I have a rough outline of where I’d like to go from here with this series but it’s still very much a work in progress. Topics I have ear marked to talk about include data representations, enemy behavior, scrolling, collision detection, random number generation, player control and other assorted things. I’d love to get feedback about what people might be interested in going forward. You can leave a comment below or reach me on twitter @allan_blomquist

(This is part 1 of a 7 part series – Next)