Last month I was asked very nicely by Manchester Girl Geeks organiser Sam Headleand if I would consider making a simple platform game for their BarCamp event. So after a bit of coding with PyGame I came up with:

Super Sam World - Episode 0: Race to the BarCamp

Super Sam World - Episode 0: Race to the BarCamp

It's the simplest platformer implementation I could imagine - there's just one level with one-way platforms, a start and an exit. This was deliberate, both to make things easier for myself and to make the code simpler for anyone wanting to have a play around with it. I couldn't create something for a STEM event without releasing the code as open-source, now could I? Interestingly I found that the limited game logic made for a fun constraint to design within.

Screenshot from the game

Screenshot from the game

One hurdle that I ran up against was with the tile rendering. The level foreground is drawn using lots of repeating square graphics called tiles - a technique that 8- and 16-bit-era games used to employ to make best use of the limited video memory available at the time. Even though they were born out of hardware limitations, a lot of modern 2D games still use tiles both for the retro aesthetic and for the ease of level design this modular approach brings.

Originally I was rendering the tiles by looping over each tile visible on the screen, and using PyGame's Surface.blit to copy the appropriate graphic to the screen buffer, each frame. The problem I found with this was that it was simply too slow to draw enough 64x64 pixel tiles to cover a 1024x768 screen.

Naive tile rendering

Naive tile rendering

As an optimisation, I did the following. Firstly I changed the tile rendering to render to a second Surface instead of directly to the screen Surface. This meant I had a "clean" version of the rendered tiles even after the other screen elements (background, player, text) had been drawn each frame.

Rendering to a buffer

Rendering to a buffer

Next I had the tile rendering start with the buffered tiles from the previous frame, and shift the image in the buffer according to how far the screen had moved across the level. PyGame's Surface.scroll can be used to shift a Surface in place. By using the tiles from the previous frame, this meant that as long as the screen didn't scroll too much between frames, most of the tiles would already be drawn for the next frame.

Scrolling rendered tiles from previous frame

Scrolling rendered tiles from previous frame

Finally I only needed to blit the tiles required to fill in the gaps left by the shifted buffer. I looped over the screen tiles as before, but skipped out the ones that were completely covered by pre-rendered area. The completed tile buffer could then be used as a starting point for the next frame, and so on.

Rendering tiles to fill in the gaps

Rendering tiles to fill in the gaps

The finished game can be found on the GitHub page here:

https://github.com/Frimkron/sam-world