The Game Engine - Part 1

The game engine is the game’s framework. There are plenty already out there. For traditional games, there are the engines, the Unreal Engine, CryEngine and Unity (some of which can compile to JavaScript). Web-specifically, there are the likes of Phaser, Impact, or the more RPG focused, RPG Maker MV. If you just want to get started building games, then I strongly advise giving one of those a go.

In building my own, what I want to avoid is overly-generic code. I don’t want too many abstractions getting in the way of readability. Digging down through includes of includes, functions within objects within objects gets weary. A simple, but manageable codebase which is fairly resilient to change down the line.

Where we are so far

Phase one was to take the functionality of the game as it stood, implement the new changes to the level data (as exposed by the editor) and break up the existing code into sensible files, objects and functions. This will help as we flesh out the engine further.

Going from one main.js file, we now have the files:

  • input.js
  • link.js
  • main.js
  • map.js
  • world.js

The guts of the game now lives in a Game object:

let Game = {

    SCALE_VALUE: 2,

    init() {

        //
        this.canvas         = document.querySelector('canvas');
        this.context        = this.canvas.getContext('2d');

        // etc...

This Game object starts with an init() function which sets up a number of variables, then, like the editor, launches a sequence of Promise-based functions which ‘boot up’ the game, so to speak; load the assets, create the player, map, etc… then start the loop:

 // Initialise!
this.loadAssets()
.then( () => this.setupGame() )
.then( () => {
    console.log('Game started');
    this.lastTime = window.performance.now();
    requestAnimationFrame(this.render.bind(this));
});

I’ve moved chunks of code into their own functions. For example, all the code for handling the input was previously dumped unceremoniously in the main() loop. I moved that code into a handleInput() function which is run inside the main loop instead.

I’ve tried to make Game an object of logical functions which describe the steps the game goes through on each loop:

let Game = {
    // init
    init(),
    loadAssets(),
    setupGame(),
    scale(),

    // construct frame
    handleInput(),
    handleCollisions(),
    cleanup(),
    
    // output
    render()
}

When I moved the input handling logic into its own function, I also moved its player logic into the Link object where it seemed most fitting. So, instead of directly affecting the player position variables like:

this.player.x += speed;

we should have it handled by the object it affects:

this.player.moveRight();

The Input objects stays mostly the same but I decided to change how the key inputs were stored and accessed. Instead of:

var key = [0,0,0,0,0];

It now uses:

let Key = {
    UP:     0,
    DOWN:   0,
    LEFT:   0,
    RIGHT:  0,
    ...
}

Which makes the code more intuitive. So

if( key[3] ) {
    this.player.moveLeft();
}

Becomes

if( Key.LEFT ) {
    this.player.moveLeft();
}

Finally, I wanted to talk about globals. Normally, these are a sign of bad code smell. But in games, these can be perfectly fine, especially when a site is going to do nothing but run this game. There is no chance of variable name clashes because its all my code. Sometimes there are just variables which are needed all over the engine, e.g:

const SCREEN_WIDTH      = 256;
const SCREEN_HEIGHT     = 224;

const ROOM_WIDTH        = 256;
const ROOM_HEIGHT       = 176;

const TILE_SIZE         = 8;

Anywhere in my code I can call TILE_SIZE and it’s fine.

Next steps

This is obviously just the start. But I’m happy with the level of abstraction so far. A good internal API that I can build on which will isolate code updates to specific areas and ripple out over the codebase.

In the next part we’ll be looking at new objects which handle other aspects of the game along with re-writing some of the functionality to better support the updated level data.

Progress

Check out the editor (with added fill mode and improved export) on version 0.9

Try out v0.9 of the game itself which uses the new editor level output.

Or see all the source code on Github.