JavaScript 2D Game Engine – Building reusable code for our JavaScript games

December 2nd, 2019 7:00 am |  by David.Reid  |  Posted in Development Log

Hello fellow programmers. In this article, we’re going to create a simple JavaScript 2D game engine based on what we’ve learned so far from JavaScript Pong and JavaScript Breakout. Don’t worry, this game engine will be very basic and its only real purpose is to prevent us rewriting the same code over and over again. We wont be writing some monolithic engine that covers everything.

This simple game engine mostly covers code that handles the game loop and thus, we will name our JavaScript 2D game engine GameLoop.

Also, in the last two articles we kind of coded as we went along. That is not necessary for this article. We’ll discuss the relevant sections of code, what they do and why we’ve added them to the engine. You can grab the game engine code from here.

So, grab a coffee, energy drink or whatever it is you drink and lets get started.

Prerequisites

Before we begin, I’m going to assume that a) you are a programmer and b) that you are familiar with JavaScript.

All the source code you need for our JavaScript 2D game engine is in this tutorial and packaged up in our source code so a knowledge of JavaScript and for that matter programming, is not entirely necessary but it sure will make a lot more sense if you are a programmer. Just saying.

Also, any editor will do the trick. Notepad is all you need but an editor like Visual Studio Code, Atom or Emacs is far superior. I’m using Visual Studio Code because its awesome but its not a requirement, so feel free to use whatever editor floats your boat.

The game engine will run on a browser. I’m using Chrome. I haven’t tested the game engine on any other browser. So, if you can use Chrome then you should. And, the game engine will run using the file:// protocol so there is no need to host the game engine on a local server. Simply open the Index.html file with your Chrome browser.

JavaScript 2D Game Engine requirements

As with any project, the first place to start is with some basic requirements/objectives. In this case we are building a very simple JavaScript 2D game engine that covers the code needed to start the Game loop. Lets list out what we want to achieve.

The game engine will…

  1. run the game loop
  2. handle key registration
  3. handle resource loading

And that is it. We have a fairly simple set of objectives for this game engine. As mentioned earlier, we want the engine to handle the game loop. We also want it to manage user input which for the time being is key registration and finally, we want the engine to manage resource loading. The latter is an extra addition that we haven’t used for Pong or Breakout but we will need it in our next game so now is a good time to introduce some simple resource management.

Okay, with requirements/objectives out of the way, lets take a look at the file architecture for our JavaScript 2D game engine.

JavaScript 2D Game Engine file architecture

In this iteration of our game engine, there are five files. Four JavaScript files and one HTML file. What follows is a brief introduction to each file and its role in the game engine.

  1. Index.html: This file is the main entry point for our browser and triggers the game loop when the page has loaded and all the resources are initialised and ready for use.
  2. Index.js: This file contains the game loop and starts running when the Index.html file is loaded. This file is completely generic and will require no modification. It simply manages the game loop, handles key listening for starting and quitting the game. And finally, loads game resources before the game loop is triggered.
  3. Register.js: The register file contains a simple global object called Register which holds game meta data, registered keys, registered resources and access to an instance of the Game and Graphics class.
  4. Game.js: This file contains a class called Game which will contain the game specific code. When creating a game, most game specific code will be managed from here.
  5. Graphics.js: This file contains a class called Graphics. The graphics class contains some methods for drawing rectangles/squares, circles/ellipses and printing text on the HTML5 canvas.

Okay, that is our brief overview of the JavaScript 2D game engine file architecture. Lets dive deeper into the code to see what is happening.

The HTML5 Canvas

Every game needs a screen to display and update the game. For JavaScript games that screen is a canvas object. We’ve covered this before in both JavaScript Pong and JavaScript Breakout. But, lets briefly cover what happens here. Below is our Index.html file.

<!DOCTYPE html>
<html lang="en">

    <head>

        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">

        <script src='Game.js'></script>
        <script src="Graphics.js"></script>
        
        <script src="Register.js"></script>
        <script src='Index.js'></script>
        
        <title>Game Loop</title>

    </head>

    <body onresize="ResizeCanvas();">
        <canvas id="canvas" width="600", height="600"></canvas>
    </body>
    
</html>

This is our entry page for the game. It’s a fairly standard html page with a header, a body and references to the required scripts. The important line in our file is the following.

<canvas id="canvas" width="600", height="600"></canvas>

This is our canvas object. And our Index.js file will create a reference to this canvas object when it loads. And at that point, our game engine code can modify the canvas to meet our needs (more on that later).

For our purposes, all you really need to know is that the canvas element is where we draw our game. But, if you want to know more about the HTML5 Canvas API then I suggest you read up on it here.

But for now, lets move on to the Register.

An overview of the JavaScript 2D game engine Register

For this version of the JavaScript 2D game engine I wanted the engine to setup key handling however, I didn’t want to specify which keys would be used as that is typically determined on a game to game basis. Likewise, I wanted the game engine to load resources, but again what resources are loaded is determined on a game to game bases.

The obvious solution was to create a registry that the engine would use to reference game specific resources and keys. And, that is the role of the Register object which is stored in the Register.js file.

Lets briefly go over the code.

const Register = {

    Title       : "GameLoop",
    Version     : "v0.1",
    Background  : "#313639",
    Foreground  : "#f5f5f5",
    ResourceDir : "assets",

    Game: new Game(),
    Graphics: new Graphics(),

    Keys: {
        Start:  { id: "start", key: "s", keyCode: 83 },
        Quit:   { id: "quit",  key: "q", keyCode: 81 },
        Menu:   { id: "menu",  key: "m", keyCode: 77 },
    },

    Resources: [
        { id: "player", var: playerPic = document.createElement('img'), file: 'player.png'},
    ]
}

The Registry meta data

The registry contains some simple meta data that is useful for the game. That meta data is shown below.

Title       : "GameLoop",
Version     : "v0.1",
Background  : "#313639",
Foreground  : "#f5f5f5",
ResourceDir : "assets",

We use this meta data for setting the page title and color. We also use it to specify the version and where we store our assets. For now, assets will primarily be images for our game actors but in the future it could be CVS files for maps, etc, etc.

Class Registration

I’ve also used the registry for storing classes that may be required across the entire scope of the game. Yes, these are nasty little global classes but for the time being it works. Will it need changed in the future? Possibly, but for now it works, so why not ride that wave until it hits the rocky, dangerous, life threatening coast ;-).

For the moment, the Graphics class and the Game class are registered so that they can be referenced by all the engines components.

Registering keys in our JavaScript 2D game engine

So lets start by looking at the key registering code.

Keys: {
        Start:  { id: "start", key: "s", keyCode: 83 },
        Quit:   { id: "quit",  key: "q", keyCode: 81 },
        Menu:   { id: "menu",  key: "m", keyCode: 77 },
    },

As you can see, we have registered three keys for starting the game, quitting the game and displaying the menu. When the game is loaded, the engine runs, listens for key events associated with the registered keys and changes the game state accordingly.

If we want to change which key starts the game, we do so in the registry and the game engine will then react to events triggered by the new start key. In short, the code that actually listens and handles start key events never changes. Which is kind of cool.

Of course, there will be more keys to add to the game but we will discuss that later.

Registering resources in our JavaScript 2D game engine

Again, lets start by looking at the resource registration code.

Resources: [
        { id: "player", var: playerPic = document.createElement('img'), file: 'player.png'},
    ]

So, this resource is there for demonstration purposes only. This iteration of the JavaScript 2D game engine has no player object. But, the engine will load the resources that we have registered in the registry. Furthermore, it wont run the game until all our resources are loaded (more on that later). But for now, all you need to know is, if you want to add a new resource to the game. Register it here and the game will load it.

And that pretty much covers our register. We will return to it when we discuss the code that actually references the register for meta data, keys and resources. In the meantime, lets move on to the Graphics class.

An overview of the Graphics class

For both Pong and Breakout, the player, the bat, the ball, the bricks, the net, etc, etc was created using the canvas context methods fillRect, fillText and arc. Basically, we drew a ton of rectangles and circles. The code required to do this never changes and therefore becomes a classic candidate for generalisation.

With that in mind, it makes sense to create a Graphics class that can be called upon to draw shapes and text when required. Let’s look at the code.

class Graphics {

    constructor() { }

    colorRect(topLeftX,topLeftY, boxWidth,boxHeight, fillColor) {
        context.fillStyle = fillColor;
        context.fillRect(topLeftX,topLeftY, boxWidth,boxHeight);
    }
    
    colorCircle(centerX,centerY, radius, fillColor) {
        context.fillStyle = fillColor;
        context.beginPath();
        context.arc(centerX,centerY, 10, 0,Math.PI*2, true);
        context.fill();
    }
    
    printText(text, xPos, yPos, size, font, color) {
        context.fillStyle = color;
        context.font = `${size} ${font}`;
        context.fillText(text, xPos, yPos);
    }
}

You’ll recognise this code if you followed the previous two tutorials I created for coding Pong and Breakout. Basically, from the word go, the above code has been used for the games we’ve developed so far. And, we can say with certainty that it will be called upon again.

That is why it has been added as a class and registered in our registry. And, it can be used as demonstrated below.

Register.Graphics.colorRect(0,0, canvas.width, canvas.height, Register.Background);

The above code draws a rectangle over our canvas.

Incidentally, the engine itself doesn’t make much use of this class. But, it will be used heavily during game instantiation and it may well be used more in future iterations of the engine. With these points in mind, it makes sense to create a Graphics class for our engine from day one.

An overview of the Game class in our JavaScript 2D game engine

So lets start this section by looking at the Game class.

class Game {

    constructor() { }

    Init() {
        // Do your init stuff here.
    }

    Reset() {
        // Resets your game here, i.e, player score, status, etc.
    }

    GameReady() {
        // Do your game ready stuff here.
    }

    GameEnded() {
        // Do your game ended stuff here.
    }

    DrawEverything() {
        // Do all your draw stuff here.
    }

    MoveEverything() {
        // Do all your move stuff here.
    }

    // Put all your game specific key mappings here.
    KeySet(event, setTo) {
        // Do all your key specific mappings here.
    }

    ResizeCanvas() {
        // Do all your game resize canvas specific stuff here.
    }
}

As you can see, the class is basically a collection of empty methods. And, I guess the obvious question is why? Why create an empty class that does nothing? Well, I guess the best way to describe this is a separation of concerns.

Separation of concerns

In this early stage, our JavaScript 2D game engine is concerned with three things that occur regularly in the game development process. Loading resources for our game, managing key events and managing the game loop but we don’t need to know the specifics of the aforementioned processes.

For example, we know that our game will contain art for our game actors but that art will differ from game to game. We know that our player will interact with the game via keys on the keyboard, but the key commands may differ from game to game. And we know that each game will have a game loop but what the game loop renders and updates will differ from game to game.

With the aforementioned in mind, we want our engine to work on the generic code whilst ignoring the game specific code. This is known as separation of concerns and a simple way to achieve that is by creating a class that is responsible for the game specifics. And that is where the Game class comes in.

The rough and ready Game class

Of course, in C# or Java, we’d use Inheritance and/or composition for our Game class. But hey, this is JavaScript, we don’t even declare variable types so its crazy to think we would even entertain the idea of Inheritance or composition. We could with prototype delegation and functional inheritance but this engine itself is a prototype so for now we’ll stick with a rough and ready empty Game class.

So lets discuss how we’ll use this empty class in our game development.

Separating concerns with the Game class

Let’s start by heading back to our first game. JavaScript Pong. In that game we had a game loop as demonstrated below.

function gameLoop() {    
    
    drawEverything();
    moveEverything();
}

In the above code, gameLoop refers to two functions called drawEverything and moveEverything. These two functions contained the game specific code. For example, below is the moveEverything function for JavaScript Pong.

function moveEverything() {
    
    if ( showWinningScreen ) {
        return;
    }
    
    ballX += ballSpeedX;
    ballY += ballSpeedY;

    checkBallXCollisions();
    checkBallYCollisions();

    paddle2Movement();
}

As you can see, it contains code that is specific to JavaScript Pong. And, while we’re here, below is the moveEverything for JavaScript Breakout.

function moveEverything() {
    ballMove();
    ballPaddleHandling(); 
    ballBrickHandling();
}

Again, as you can see, it contains code that is specific to JavaScript Breakout. And, this would be the same for drawEverything. In short, our game loop will draw and move everything but what it draws and how things move is specific to any given game.

In short we want a generic game loop that ignores the specifics of any given game.

An example of a generic game loop using the Game class

So lets consider the following example.

function gameLoop() {
   Register.Game.DrawEverything();
   Register.Game.MoveEverything();
}

In this example, our gameLoop is calling the DrawEverything and MoveEverything functions from the game class referenced in the register.

In this scenario, the loop code never changes and the engine itself has no methods for drawing or moving game actors. It simply delegates that job to the game class referenced in the registry. And, the job of the developer will be to update the game class with game specific code. Thus, the game engine doesn’t and never needs to know what its moving or drawing (aka separation of concerns).

The Game class is basically a contract

Okay, at this point, we’ve talked an awful lot about a class that contains no code. However, before we move on, I just want to iterate that the engine expects the game to carry out game specific functions. It’s basically a contract. And below is an overview of the functions that make up that contract.

  1. Init: the game needs to initialise its state
  2. Reset: the game needs to reset its state
  3. GameReady: the game needs to have a game ready screen
  4. GameEnded: the game needs to have a game ended screen
  5. DrawEverything: the game needs to draw its actors
  6. MoveEverything: the game needs to move its actors
  7. KeySet: the game needs to react to game specific key events
  8. ResizeCanvas: the game needs to react to a canvas resize

And that is pretty much it. As mentioned before, that was a lot of fluff about an empty JavaScript class but it’s pretty important, and we’ll no doubt mention it again before the end of this article. But for now, lets move on to the nuts and bolts of the engine.

The Index.js file – the code that makes our JavaScript 2D Game Engine tick

Okay, until this point, we’ve only really discussed the peripherals of our engine. Now its time to look at the code that actually makes the game engine tick. So lets get started.

Our engine basically does four things. It initialises the browser window and the game keys. After that, it loads the resources and once the resources are loaded, it runs the game loop. In short,

  1. Initialise the browser window
  2. Initialise the keys
  3. Load the resources
  4. Run the game loop

We’ll talk about each step in sequence.

Initialising our browser window

Lets look at the code first and then discuss what is going on.

function InitWindow() {
    canvas = document.getElementById('canvas');
    context = canvas.getContext('2d');
    document.title = Register.Title + " " + Register.Version;
    document.body.style.margin = "0px";
    document.body.style.padding = "0px";
    document.body.style.background = Register.Background;
    document.body.style.color = Register.Foreground;
    ResizeCanvas();
}

So, the function is fairly self explanatory, it initialises the browser window. For our game engine, this means creating a reference to the canvas and context objects. As well as dealing with some meta data, like title, background and foreground colours. Note, that the game engine uses the Register for the latter.

The last line of code resizes the canvas which is another game function as shown below.

function ResizeCanvas() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight - 6;
    Register.Game.ResizeCanvas();
}

As you can see, our resize canvas resets the canvas to the size of the browser. And if you recall, ResizeCanvas is called when the user changes the browser size. Also, our engine expects our game to deal with this too and invokes the following line of code.

Register.Game.ResizeCanvas();

And that’s that. Our engine, initialises the canvas, sets some meta data, resizes the canvas and tells our game class to handle a canvas resize. Let’s move on.

Initialising our game keys

Okay, initialising our keys involves for different functions that collectively set our event listeners and relates different key presses to different actions. Lets go through the code of each function and discuss how they are used in our JavaScript game engine. First up is the InitKeys function.

function InitKeys() {
    document.addEventListener('keydown', keyDown);
    document.addEventListener('keyup', keyUp);
}

As you can see, this is a fairly straightforward function that sets event listeners for key presses and key releases. When those event listeners are triggered they invoke either the keyDown or keyUp functions which are shown below.

function keyDown(event) {
    KeySet(event, true);
    event.preventDefault();
}
function keyUp(event) {
    KeySet(event, false);
}

As you can see from the code above, the keyUp and keyDown functions basically do two things. They pass on the event that was triggered and a Boolean that represents whether or not the key was pressed or released to the KeySet function. The aforementioned function – KeySet –  is where we map our key events to game actions.

Mapping our key events to game actions

As usual, the best place to start is the code. Below is our KeySet function which maps our relevant key events to game actions.

function KeySet(event, setTo) {
    
    if (event.keyCode == Register.Keys.Start.keyCode && gameStatus == GAME_READY) {
        SetGameToRunning(setTo);
    }
    if (event.keyCode == Register.Keys.Menu.keyCode && gameStatus == GAME_ENDED) {
        SetGameToReady(setTo);
    }
    if (event.keyCode == Register.Keys.Quit.keyCode && gameStatus == GAME_RUNNING) {
        SetGameToQuit(setTo);
    }

    Register.Game.KeySet(event, setTo);
}

As you can see from the code above, we accept two parameters. The first contains our key event – event – and the second – setTo – is a Boolean that represents whether the key has been pressed or released.

In the first iteration of our game engine, we have three states. The game ready, game running and game ended. Game ready, represents a state where the game is ready but not in the play mode. Game running means the game is being played and game ended means the game has been played but is now over.

The first three if statements in our code deal with key presses related to those aforementioned states. They are as follows.

  1. If the game is ready and the player pressed the start key then set the game to game running.
  2. If the game has ended and the player pressed the menu key then set the game to game ready.
  3. And finally, if the game is running and player pressed the quit key then set the game to game ended.

These three conditions allow our player to change the game and you’ll have noticed that the keys used to change those states are registered in our Register.

Finally, the last line of code defers further key mapping responsibility to our specific game instance.

Deferring game specific key mapping to our game class

The last line of code in our KeySet function is shown below.

Register.Game.KeySet(event, setTo);

This line of code defers game specific key setting to our game class. Note, we access the game class using our register.

Why do we do this? Well again, this is basically a separation of concerns. Our game engine can with certainty deal with key presses that relate to game states because every game, no matter the genre will have game ready, game running and game ended states. And therefore, that code can and should be generic. However, game specific key events such as player control is specific to the game and therefore should be dealt with by the game itself.

To be fair, as we continue to use the engine to create games, we may see fit to extend the key mapping responsibilities or our JavaScript 2D game engine but, for now the aforementioned is enough.

Lets move on to registering and loading resources.

Registering and loading resources for our JavaScript 2D Game Engine

To this point, we haven’t used resources in our game. For example, in Pong and Breakout all the graphics were create using the Canvas API. However, it is reasonable to expect that we will want to load images in future games and I intend to use images in my next creation.

With the aforementioned in mind, it seems reasonable that we consider loading resources now rather than later. So lets start by thinking how that might work.

Thoughts on resource loading for our JavaScript 2D game engine

Obviously, if we want to use resources such as images in our game, then it would be useful for those images to be loaded before the game starts. That way our game can access them as soon as the game loop begins.

Loading images first and then running the game loop is a fairly straightforward concept. However, at the coding stage, it’s also easy to get wrong as image loading in JavaScript is done asynchronously. When JavaScript encounters a command to load an image, It loads the image in the background whilst continuing to process the rest of your code. In short, JavaScript could be loading images whilst running the game loop which in turn, would have a detrimental effect on our game. And when I say detrimental, I mean a catastrophic failure in both game design and game play.

Avoiding catastrophe is probably a really good idea so we’ll want to design a loading mechanism that ensures our images are loaded before we run the game loop.

If you are familiar with JavaScript you might be screaming Promises! Promises! However, we’re going to do something even simpler. We’re going to use a simple countdown.

But before we can load images, we need to register them first.

Registering images in our JavaScript 2D game engine

So we discussed this earlier but lets quickly take another look at the code that registers resources in our JavaScript 2D game engine.

Resources: [
    { id: "player", var: playerPic = document.createElement('img'), file: 'player.png'},
]

Basically, the register creates an array of resources that we can access in our game. Thus, in our code we can access the number of resources we have for our game with the following line of code.

// This accessor will tell us how many resources we have registered with our game.
Register.Resources.length;

In the above case, we only have one image but in a real game, there could be dozen of images and audio files registered here. But, for now, the important point is that we will know prior to game launch how many resources we have.

Let’s move on to the loading part.

Loading resources in our JavaScript 2D game engine

When we load the page that contains our game – Index.html – we want to start loading resources as demonstrated in the code below.

window.onload = function() {
    InitWindow();
    InitKeys();
    LoadResources(); // GameLoop triggered by LoadResources
}

The last line in the above code calls the LoadResources function. This function starts the resource loading process in our game engine. Lets take a look at the code.

function LoadResources() {

    resourcesToLoad = Register.Resources.length;

    for (let i = 0; i < Register.Resources.length; i++ ) {
        if (Register.Resources[i].var != undefined) {
            beginLoadingImage(Register.Resources[i].var, Register.Resources[i].file, Register.ResourceDir);
        }
    }
}

As you can see, the function sets the resourcesToLoad variable to the number of resources we have registered. It then runs a for loop that individually loads our images. It does so by calling the beginLoadingImage function which is the next step in our loading process.

Loading individual images

As shown above, loading individual images is done with the beginLoadingImage function which is shown below.

function beginLoadingImage(imgVar, fileName, directory) {
    imgVar.onload = launchIfReady;
    imgVar.src = directory + "/" + fileName;
}

This function basically loads our image. However, a very important line of code in this function is the following.

imgVar.onload = launchIfReady;

In JavaScript, we can assign a function to execute once an image has successfully loaded. And in the above code, we do just that. We tell our code to run the launchIfReady function once our image has loaded.

Launching when ready

As mentioned above, once an image has been loaded, we tell our image to execute the launchIfReady function. Lets take a look under the hood of that function.

function launchIfReady() {
    resourcesToLoad--;
    if (resourcesToLoad == 0) {
        gameLoop();
    }
}

As you can see the function is fairly simple. The function has been triggered by an image that just loaded, therefore we can decrease the value of the variable – resourcesToLoad – by one. Once we do that we check to see if our resourcesToLoad variable is zero. If it isn’t zero, we do nothing as we know there are more resources to load. If it is zero then we can invoke the gameLoop function which starts the game.

To reiterate what happens here, if there are ten images to load, the – launchIfReady – function will be invoked ten times. And on the tenth invocation, the resources to load value will reach zero and thus the game loop will be triggered.

And that is it. That is how we ensure our game loop has loaded its images before we start the game loop.

The Game state and the game loop

The final piece of the puzzle is the game loop. If you followed my tutorials on JavaScript Pong and JavaScript Breakout, you’ll be familiar with the game loop. The game loop is the part of the engine that actually runs the game at thirty or sixty frames per second. Its the code that turns images on the screen into moving actors. And because so much of what makes our game, specific to each individual game, there isn’t much to see in the game loop. Nevertheless, lets do what we done throughout this article and start by looking at the code.

function gameLoop() {
    
    console.log(Register.Title + " " + Register.Version);
    
    Register.Game.Init();
    
    SetGameToReady();

    setInterval( function() {

        // Cover canvas with our background color.
        Register.Graphics.colorRect(0,0, canvas.width, canvas.height, Register.Background);
        
        switch(gameStatus) {
            
            case GAME_READY:
                Register.Game.GameReady();
                break;

            case GAME_ENDED: 
                Register.Game.GameEnded();
                break;
            
            case GAME_RUNNING:
                Register.Game.DrawEverything();
                Register.Game.MoveEverything();
                break;
        
            }}, 1000/FRAMES_PER_SECOND);
}

As you can see, the game loop is fairly straight forward. However, you will have noticed that there are several different game states which are Game Ready, Game Running and Game Started. Lets look at each state more closely before we discuss the game loop as a whole.

Game Ready

In this iteration of our JavaScript 2D game engine, the game ready state indicates that the game loop is running but the game hasn’t started yet. For example, in a typical game, the game ready state would be the point where you display a start screen with key commands.

In our engine, when we are in the game ready state, we shift responsibility to the Game class as demonstrated in the code below.

case GAME_READY:
                Register.Game.GameReady();
                break;

Game Ended

The game ended state indicates that the game has run its course because the player has completed or lost the game. In a typical game, this is where you would see a game over message and maybe a player score. As with the game ready state, responsibility for this state is shifted to the specific instance of the game with the following lines of code.

case GAME_ENDED: 
                Register.Game.GameEnded();
                break;

Game Running

And finally, the game running state indicates that game is currently being played. This is the state where the player actually plays the game. This is where you actually blast those asteroids or destroy those pesky invading aliens. The code for this state is demonstrated below.

case GAME_RUNNING:
                Register.Game.DrawEverything();
                Register.Game.MoveEverything();
                break;

And again, the game loop shifts responsibility to the specific games DrawEverything and MoveEverything functions that you will be familiar with if you have followed my previous tutorials on building JavaScript games.

With the game states covered, lets look at how our game loop works.

How the game loop works

When all the resources are loaded and the game loop is invoked, the first thing it does is print the game title and version in the console. Obviously, this has no impact on the actual game play and could easily be omitted.

The second thing it does is call the games init function with the following line of code.

Register.Game.Init();

After the game is initiated, it sets the game to the ready state with the following line of code.

SetGameToReady();

The SetGameToReady function resets the game and changes the state to ready as shown below.

function SetGameToReady() {
    Register.Game.Reset();
    gameStatus = GAME_READY;
}

At this point, we are ready to invoke the loop which is done by using the JavaScript setInterval function which continuously runs a specified function at specified intervals. In our case, that function contains our game state code, and the interval is sixty frames per second.

And that is it. That pretty much covers our first iteration of the game engine. However, lets not kid ourselves here, this first iteration is neither complete or battle tested. There are many features we could add and I think its worth covering them before we finish up this article.

Useful features we’ve omitted from this iteration of our JavaScript 2D game engine

So, saying we’ve omitted some features from this iteration of our JavaScript 2D game engine is an understatement. There is hardly any features in this version which is intentional. I want the engine to be developed based on previous game building experiences. And, apart from loading resources, this iteration of the engine contains everything we need to build both Pong and Breakout which covers everything we’ve developed so far.

Nevertheless, there are some features which would obviously be a benefit going forward. Yet, they are not present in this iteration of the engine. What follows is what I thought about adding and my reasoning for omitting them at this stage.

JavaScript Modules

Our code is heading towards modulation. We have classes, and some clear separation of concerns within our code. But at the moment, we are not taking advantage of JavaScript modules.

Why? At the moment, I wanted to focus on game logic rather than software architecture. Also, I think the structure of the JavaScript 2D game engine will change drastically as we move forward. For example, it works well for Pong and Breakout but how will it perform for a clone of Asteroids or Defender? How would it work for a Donkey Kong styled game? The engine is not battle tested which is why I don’t want to waste too much time focusing on code that isn’t related to game logic. Also, when I get to a point where I think the engine is logically sound I can turn my attention to modulation. The latter might be painful, but I can live with that.

Sprites

At the time of writing this article, I’m also developing a clone of Asteroids with this iteration of the game engine. Unlike Pong and Breakout, Asteroids has more game actors, it has the player, the asteroids and the random alien that flies by shooting green alien shit at you. Those actors, whilst different, still have many common attributes and behaviours and could benefit from some generic Sprite class. This is obvious yet we’ve ignored that in our first iteration.

Why? Again, our previous experiences haven’t demanded a Sprite class or sprite service and thus has been ignored in this iteration of the JavaScript 2D game engine. But, I can say with certainty that the next iteration will deal with Sprites. It may not deal with animated Sprites but it will have code that captures the generic attributes and behaviours of game actors. And again, the reason I can say that with certainty is because I have pretty much written the first iteration of a sprite in my current project.

Tiles

Many classic arcade games are based on tiles. If you are developing 2D games, you’ll at some point you’ll encounter tile-based video games. A good 2D game engine will contain functionality for building and mapping a tile-based game but yet again, this functionality is missing from our game.

Why? Same answer, we haven’t encountered that requirement yet and therefore haven’t built it. However, a future iteration of the JavaScript 2D game engine will deal with tiles but it wont happen in the next iteration.

And there you have it. In the authors opinion, Modulation, Sprites and Tiles are the most glaring omissions from the current iteration of our engine. And, there is more but I think the best approach is to build the game first and game engines second.

So what is next?

This JavaScript 2D game engine is obviously not battle tested

So we’ve built the first iteration but it’s obviously not battle tested. And therefore, the next step is to build games using the game engine. As I mentioned earlier, that process has already begun. I’m currently developing a clone of Asteroids using this iteration of the JavaScript 2D game engine and I’ve already made some changes to the engine which I will discuss in a future article.

My plan is for the future is as follows.

Build a game, update the engine, repeat.

And that’s it for this article. Have fun and keep coding.