10 Mar

How levels are generated in Spaceman 8

All levels in Spaceman 8 are procedurally generated and get larger and more complicated as the game progresses. PICO-8 has technical limitations so the map generator has to be relatively simple. The level generator works in two steps: first by generating the general structure of the level and then adding superficial details on the map that is eventually drawn on the screen. The map contains randomly placed objects that are often found in the ends of the map corridors.

The seed map

The seed map has two types of cells: “open” and “closed” cells. Map is initialized with closed cells.
All level objects (the collectables and the exits) are treated similarly.

  1. Start with single open cell in the center of the map
  2. Position all objects in the starting cell
  3. Pick a random cell on the map using these rules:
    • The cell has to be closed
    • Zero to one diagonal (NW, NE, SE, SW) neighbors have to be open
    • 1 or 2 cardinal (N, E, S, W) neighbors have to be open
  4. Change the cell type open and move an object from any of the neighboring cells or a randomly picked object in the new open cell
  5. Repeat from step 3 until map is large enough

Demo cart and code

You now have a branching, mazelike map that has all open cells connected at least from one direction. The objects should be positioned (mostly) in the ends of the corridors, further from the starting position (this of course depends on luck and you should fiddle with it if you want to make sure the object locations are always challenging).

Making it pretty

The map is upscaled so that each cell now takes twice the cells vertically and horizontally. There is a random change that a cell flips its state if the neighboring cell is of the opposite type. The resizing is done again and again so that the level corridors are suitably wide (at first they are only one tile wide, then two, four etc.). The randomness from the resizing steps carries over and adds first large and then (relatively) smaller random variation to the edges.

The visible map is built from the data using a tileset that has tiles for different edge/side combinations.

The seed map is scanned cell-by-cell and if the current tile is closed, a bitmask is built from the four neighboring seed tiles in the cardinal directions (see the linked cart for working code). The bitmask tells us the tile we want on the visible map. E.g. if all neighboring seed tiles are closed, the bitmask will be binary) 1111b = 15, we know we want to use tile 15 which is a fully filled tile. If one of the sides is open, let’s say to the east, the mask will be 1101b = 13 and we will use tile 13.

If the current seed tile is empty, the visible map will simply have an empty cell. All of this is very easy to implement when just following instructions (basically just make sure the tile order is the same and it will work) but here is a good article about the technique in case you want more details.

Demo cart and code

Finally, the visible map has some random variations added on it: empty cells have a random change to have a non-black background tile and the fully filled tiles have a random change to have a pebble tile and so on. This randomness doesn’t change the level geometry.

03 Apr

How does Pico Racer work?

After I released Pico Racer, lots of people have thought it looks nice and is an achievement. I don’t think it does anything special or pushes the limits of the Pico-8. Here I try to explain the small tricks I used in the game. If you want a general tutorial about pseudo 3D racers in general then please check out Louis Gorenfeld’s excellent page about the subject, I try to focus on Pico-8 specific stuff.

  1. Road rendering
  2. Sprite rendering
  3. Car rendering and sprite perspective
  4. Night levels and color fades
  5. Pico-8 performance

Road rendering

Usually, with pseudo 3D racers, the road is drawn using a simple trick: there is a full screen graphic of a straight road with perspective and every horizontal line gets shifted left or right, more the further down the road the current horizontal line is. This makes the road look like it’s curving. Palette tricks are used to create the familiar stripes that give an illusion of motion.

With Pico-8, there is no space for a precalculated road asset but we can draw scaled sprites instead. The road in Pico Racer is simply a series of one pixel high sprites scaled by distance, one for both track edges and two used to draw the road surface. The road is a true textured plane instead of a distorted image. Curves works similarly to the above classic method.

Additionally, when rendering the road (from bottom of the screen to the middle of the screen) we iterate along the track data. This is very approximate because we just take the screen Y, calculate the Z and figure out which part of the track the horizontal line belongs to. A more accurate way would be to iterate Z instead of the screen space Y and when Z projected to the screen gives a different screen Y coordinate, we would draw the horizontal line. But going along Y gives a good enough effect, the player will get a good enough hint what is coming towards him.

The road X position on screen is stored for each Y coordinate which we will use later to render the sprites correctly along the road. Note: this gives a slightly crude motion to sprites when they are far away because the X offset are stored per each screen Y coordinate instead of Z. As long as the action is fast, the player will not notice anything.

The road texture graphic used per line is selected so that the further away the line is, the lighter the colors in the texture. Dithering is used to mask the exact position where the texture changes.

One texture

One texture

Multiple textures

Multiple textures

Dithered textures

Dithered textures

This is done because of two reasons: in nature, colors get lighter the further away something is (actually, tint to sky blue beacuse there is air between you and the faraway object) and also so that the road sides and lane markings won’t strobe as much.

PICO-8_5

Lots of flicker

Less flicker

Less flicker

Sprite rendering

Sprites are rendered basically using the same idea as for the horizontal road lines. First, Z is used to determine the zoom factor. Secondly, sprite X position (on screen) is divided by Z and the road offset for the sprite screen Y coordinate is used to offset the sprites so that they are arranged along the road.

Car rendering and sprite perspective

There is one very important and unavoidable problem with sprites and 3D: the sprites always face the camera. This is not a problem with orthogonal 2D projection, since there is only one way the camera looks at. Everything is on one flat plane. But with a perspective projection the sprites start to look like they are always rotated to face the camera unless you take the time and draw multiple versions of the sprite from different angles. This is how Origin’s Wing Commander and Lucasarts’ Their Finest Hour work. But this takes a lot of effort and more importantly: it needs a lot of space for the sprites.

In Pico Racer, sprite perspective is handled so that most sprites (trees, warning signs) have no perspective. This works reasonably well due to two facts: the objects are two-dimensional (warning signs) AND they are always located to the sides so that the angle to them would be pretty much the same at all times. Only the player car and the opponent cars have visible perspective, since that’s what you look at the most. Also, the cars (especially the player car) have a lot of sideways motion which would immediately make the cars’ perspective look odd.

No perspective

No perspective

Sprite perspective

Sprite perspective

Full perspective

Full perspective

The cars use two tricks to fake a convincing perspective: firstly, they have multiple versions of the sprites. Secondly, the cars are built of a number of separate sprites located at slightly different distances. I borrowed this idea from Lankhor’s Vroom and it works well with cars that have no flat sides and instead have clear “sections” – just like F1 cars have. The cars have the rear sprite, the front sprite and a middle section. When the car moves sideways, in addition to showing the sprite from an angle, the rear wheels have some sideways motion against with respect to the front wheels. And, while not a perspective thing, the front section is moved sideways when the player turns left or right, when the road has a curve or when the car bounces giving the illusion the car is yawing and pitching.

Parts of the car

Parts of the car

Night levels and color fades

The color fades in Pico Racer are done using the palette mapping feature in Pico-8 and a few lookup tables. Basically, the tables tell which color to use for each of the 16 colors at a set brightness. Hand picking the colors gives the possibility (actually, with the fixed palette the fade will be tinted) to tint the fades so that they mimic sunset and so on. The road, sky and the sprites use a different lookup table each so that e.g. the road has markings visible even at pitch black and the sky and the horizon have a slightly different fade curve (because nature works like that).

Palette lookup table

Palette lookup table

It all works something like this:

PALETTE={
 {1,1,1,1,2,2,2,0},
 ...
}

FUNCTION SETFADE(LEVEL)
 FOR C=1,15 DO
  -- ALL COLORS WITH VALUE C ARE NOW DRAWN 
  -- WITH THE FADED COLOR
  PAL(C, PALETTE[C][LEVEL])
 END
END

On night levels, the cars are have rear lights drawn after it is dark enough. They are simply two extra sprites drawn without using the fade lookup palette.

Pico-8 performance

The Pico-8 is more than enough to do all the math and rendering in one frame (30 FPS). In fact, only the sprites rendering seems to be a bottleneck. Mainly, when sprites are zoomed, their area grows exponentially and the area is used to calculate how long Pico-8 takes to draw the sprite. Sometimes even large but fully clipped (i.e. outside the screen or the clip rectangle) sprites slow everything down. I found the combined area of the sprites is more important than the number of sprites.

In Pico Racer, I capped sprites so that they never zoom larger than 100 %. This has minimal effect on visuals, although you will easily notice it if you know it’s happening. Since e.g. the cars suddenly stop growing as they get closer to the camera, your brain thinks the cars in fact start shrinking. Likewise, since there is a short range where all the cars are drawn equally large, a car a bit further away looks larger than a car closer to the camera, everything because your brain expects things further down the road to be a bit smaller.

As for performance, limiting sprite size gives a big performance boost since as the sprites come very close to the camera and they get larger and larger, they very quickly get zoomed 200 %, 300 %, 600 % and so on. Further away they are just a dozen of pixels for a very long distance.

Since the area of sprites is the main contributing factor, this makes rendering the road quite efficient because every horizontal line is just one pixel high even though there are four sprites per horizontal line and 64 lines total.

27 Dec

Review: Ninja Senki

A few days ago the indie game Ninja Senki was released after a year of development. In short, it’s a Ninja Gaiden inspired (a lot of pages say Mega Man but I don’t agree with that) retro platformer that looks very late 1980s and also has that late 1980s difficulty.

The game is hard. This is why it reminds me more of Ninja Gaiden (besides the theme) than Mega Man. The gameplay is very simple, in that you can only run, jump and throw shuriken. You can also double jump once while airborne and that skill is available and required from the beginning. Which is perfectly fine. The game chooses a gameplay style and sticks to it with no afterthoughts. The gameplay feels perfect and the high difficulty doesn’t feel unfair as it would with controls that were less than perfect. The game also supports gamepads.

Ninja Senki screenshot I really liked the music and sound effects, they’re pretty much 100-percent authentic (FamiTracker was used to create the sound effects which pretty much makes it sound exactly like a NES game with the chiptune background music). The graphics are simple, obviously, since they imitate the Gameboy resolution (160×144) and the NES palette limitations. In my opinion, some of the graphics could have benefited from some variation. Especially, the few first levels look very monotone and boring compared to the later levels with more colorful backgrounds. It still looks fine, though. But as far as first impressions go, I saw more interesting stuff in the trailer than in the first few minutes of the game.

There are multiple endings depending on how well you play which is great considering how hard the game is. It feels like the game knows it’s way too difficult for 2010 so it has some slack (as long as you play your eyes open, you’ll probably be able to buy a continue or two with your score) but still would like you to play a perfect game. There are no save games which I think is not too bad considering it adds to the feeling (you have to play from beginning to end in one sitting just like you did as a kid) but since people who love retro games are not children anymore and actually have stuff to do, the game should have at least some way to continue the game.

The game is free (I was a bit surprised of this) and feels great so anyone interested in challenging platformers should check it out.

10 Mar

Review: The Last Arcadian

The Last Arcadian is one of those games that in hindsight are pretty strange stuff for a public domain or shareware title. Considering even in the early 1990s a 3D shooter was automatically rather cool and thus profitable, The Last Arcadian makes you think why it wasn’t released commercially. It looks a bit dated even for a 1992 3D title but you have to keep in mind it is a one man show. A bit of money would have made it look much nicer. It could be compared to Epic but I’m not sure if that’s a compliment. I have seen worse — I have paid for worse.

(Another video below.)

The main idea is to go and destroy enemy bases while the enemy tries to do the same to you. The game is over if your base is destroyed. If you, flying a fighter, are killed, you will simply find yourself piloting a nearby fighter that was previously computer controlled. There are multiple wingmen flying around and if you watch the first video, they sometimes manage to win the game for you.

The enemy fighters are killed with lasers and homing rockets and the enemy base needs to be bombed. When you return to your base, you need to make sure nobody else is trying to dock at the same time; you have to request permission to land. You can also repair your fighter in the base and then return to the battle (to save the computer controlled fighters.) The bases can also fire cruise missiles that travel slowly and you need to escort them and also keep the enemy missiles away from your base.

Overall, the game is very nice. It is simple but has some quirky features and also some quite modern stuff like the zoom in your ship at the start of each level and the fact everything is three-dimensional. There is a feeling of immersion as the rest of the war goes on around you while you’re being resupplied — quite well done as I no longer have the imagination of a kid. If you’re a fan of the old 3D Star Wars shooters, you probably should check this out if you have an Atari ST lying around.

The game can be found on the ST Format 42 cover disk available on this page.