Founder, author, speaker
game

Space Invaders is a classic game that most of us have enjoyed as a child. I needed to build a basic “Space Invaders” visualiser for the Entelect R100K Challenge. The challenge this year prompts contestants to develop a bot program to play the game versus another bot program. Each player has a turn to move their space ship, shoot, or build a booster building. To find out more details about the challenge and the details behind the mechanics, check out the rules. In essence, each iteration (or turn) of the game is recorded in a text based map. This map is pretty readable but doesn’t really embrace the atmosphere of the game. The aim of this visualiser is to give the contestants a tangible way to visualize their matches and provide some inspiration to contestants that are making their own visualisers for the Gooey Challenge. You can download the code on GitHub.

Overview

The visualiser needs to be accessible to everyone, so it made sense that it should run in a modern browser. This means that it needed to be developed using JavaScript. Plain old JavaScript would be a mission, and since time wasn’t on my side, making use of a rending engine or game engine made sense. So from past experiences playing with HTML5 game engines, I decided to use Phaser. It’s easy to use, provides the required features and is open source.

Since each game state is recorded in a different text file, it’s pretty obvious that the minimum requirement of the visualiser is to read each game state in sequence and play them back. To make it visually appealing, each character that represents a game item should be rendered as a sprite that’s a bit more interesting.

Making Some Artwork

The first thing I decided to do is create some artwork that would represent the game. I didn’t have much time, so making low detailed sprites was the only way to go, furthermore, the low detailed sprites gives the visualiser a retro feel to it which fits in really well with the spirit of the challenge.

The game objects are represented below. Most objects are 1×1 characters in size, while some are 3×1 – this includes space ships and buildings only.

AAA: Your Ship
VVV: Opponent's Ship
XXX: Alien Factory
MMM: Missile Controller
#: Wall
-: Shield
x: Alien
|: Alien Bullet
!: Your Missiles
i: Opponent's Missiles

So here’s the artwork for each of these objects:

AAA: Your Ship

ship-player

VVV: Opponent’s Ship

ship-opponent

XXX: Alien Factory

factory-alien

MMM: Missile Factory

factory-missile

#: Wall

wall

: Shield

shield

x: Alien

alien

|: Alien Bullet

missile-alien

!: Your Missile

missile-player

i: Opponent Missile

missile-opponent

Developing the Visualiser

Now that there’s some artwork to use as sprites in the visualiser, it’s time to put everything together. Again, we have a sequence of text files that represent a game state, and some images that will represent each character in a specific game state. So the following image depicts the goal for each game state.

examplemap

The Phaser game engine allows for initialisation with a canvas width, canvas height, the type of renderer, and a name for the object. Furthermore, Phaser allows for a preload function and an update function. This is useful since the assets and game states can be loaded in the preload function, and the update function is just responsible for rendering each text-based game state.

The Phaser Game is initialised with the following. It’s telling Phaser to make a new canvas named “phaser” with the correct width and height. It’s also specifying the functions that should be used for preloading and updating.

var game = new Phaser.Game(SPRITE_SIZE * COLUMNS, SPRITE_SIZE * ROWS, Phaser.CANVAS, 'phaser', {  preload: preload,  update: update });

The preload function needs to load the game state text files, as well as the images for the sprites. Phaser conveniently handles this by allowing various types of assets to be loaded, this includes images and strings – which works out perfectly for this purpose.

In the preload function, a helper function named loadMaps(replayPath, iterations) is used to load all the maps, and the game.load.image(name, filepath) function is used to load all the images into the game.

function preload() {   loadMaps('example-replay', 52);    game.load.image('alien', 'images/alien.png');  game.load.image('factory-missile', 'images/factory-missile.png');  game.load.image('factory-alien', 'images/factory-alien.png');  game.load.image('missile-alien', 'images/missile-alien.png');  game.load.image('missile-opponent', 'images/missile-opponent.png');  game.load.image('missile-player', 'images/missile-player.png');  game.load.image('shield', 'images/shield.png');  game.load.image('ship-opponent', 'images/ship-opponent.png');  game.load.image('ship-player', 'images/ship-player.png');  game.load.image('wall', 'images/wall.png'); }

Here’s the loadMaps function. It simply iterates for the number of game state files and loads them into the game as text strings.

function loadMaps(replayPath, iterations) {   for (var i = 0; i < iterations; i++) {   game.load.text('maptext' + i, replayPath + '/' + zeroFill(i, 3) + '/map.txt');  }  }

You may notice the zeroFill() function. There exists a directory for each iteration of the game state, it is named with the iteration number and padded with zeros to make up a three character name. E.g. 1 becomes 001. The zeroFill() function is required to correctly pad the zeros for the correct game state path.

Now that the game sprites are loaded and the game states are loaded, they need to be replayed. The Phaser update function is important to accomplish this. Since Phaser handles the update rate, I needed to do some clever checks to achieve the frame rate that I wanted.

function update() {    if (game.time.now - timeCheck > FRAME_RATE_DELAY_MS) {   game.world.removeAll();   var map = game.cache.getText('maptext' + iteration);   generateMap(map);   iteration += 1;   timeCheck = game.time.now;  } }

The initial condition determines if the right amount of time has passed, if it has, then we can update the game and objects being rendered. Phaser doesn’t allow for user defined ticks, so this is necessary to delay the iterations of the game state.

Next, all the elements of the game’s world are remove, this is to prevent old objects from sticking around and obscuring the current game state.

A map string then gets populated with the current iteration’s game state from the cache that was populated in the preload function.

The generateMap(map) function uses the game state text, iterates over the string and adds the correct sprite based on the respective character. Some logic is needed for when 3 block objects and new line characters. To find out more about this, have a look at the code on GitHub.

Finally, the iteration number is incremented and the time of rendering is recorded for use in the next iteration.

And it’s that easy to create a simple but effective visualiser using Phaser and JavaScript.

Adding Juice

Part of what sets a game or visualiser apart from the rest is the subtle detail and effort put into it. Here are some ideas for improving the above visualiser:

  • Tweening transitions between each game state: As you can see, the visualiser is very jittery, this fits for the retro style of this visualiser but a differently styled visualiser will be greatly improved by simply tweening the iterations.
  • Particle effects for collisions.
  • Animations when destroying and rendering interactions between sprites.
  • Sound effects for shooting, collisions, and spawning buildings.
  • A menu system for loading different matches.
You’ve successfully subscribed to Rishal Hurbans
Welcome back! You’ve successfully signed in.
Great! You’ve successfully signed up.
Success! Your email is updated.
Your link has expired
Success! Check your email for magic link to sign-in.