9 min read

The phrase “Do not reinvent the wheel” is often heard when writing software. It definitely makes sense not to spend your time on tasks that others already have solved.

Reinventing the wheel has some real merit. It teaches you alot about the problem, especially what decision need to be made when solving it. So in this blog post we will reinvent a game engine to learn what is under the hood of most game development tools.

StopWatch

We are going to learn about game engines by creating a game from scratch. The game we will create is a variant of a stop watch game. You will need to press the spacebar for a fixed amount of time. The object is to come as close as you can get to a target time. You can play the finished game to get a feeling for what we are about to create.

Follow along

If you want to follow along, download StopWatch-follow-along.zip and extract it in a suitable location.

If you now open index.html in your browser you should see a skeleton of the game.

Create the Application

We will get things started by adding an application.js. This file will be responsible to start the game. Open the directory in your favorite editor and open index.html. Add the following script tag before the closing body-tag.

<script src="js/application.js"></script>

This references a JavaScript file that does not exist yet. Create a directory js below the root of the project and create the file js/application.js with the following content

(function(){
    console.log('Ready to play!');
})();

This sets up an immediatly invoked function expression that creates a scope to work in. If you reload the StopWatch game and open the developer tools, you should Ready to play! in the console.

Create the Library

The application.js setup the game, so we better create something to setup. In index.html above the reference to js/application.js, refer to js/stopwatch.js:

<script src="js/stopwatch.js"></script>
<script src="js/application.js"></script>

stopwatch.js will contain our library that deal with all the game related code. Go ahead and create it with the following content:

(function(stopwatch){
})(window.stopwatch = window.stopwatch || {});

The window.stopwatch = window.stopwatch || {} makes sure that a namespace is created. Just to make sure we have wired everything up correctly change application.js so that it checks the stopwatch namespace is available.

(function(){
    if (!stopwatch) { throw new Error('stopwatch namespace not found'); }
    console.log('Ready to play!');
})();

If all goes well you still should be greeted with Ready to play! in the browser.

Creating a Game

Something should be responsible of keeping track of game state. We will create a Game object for this. Open js/stopwatch.js and add the following code.

var Game = stopwatch.Game = function(seconds){
        this.target = 1000 * seconds; // in milliseconds
    };

Inside the immediatly invoked function expression. This creates an constructor that accepts a number of seconds that will serve as the target time in the game.

Creating a Game object

The application.js is responsible for all the setup, so it should create a game object. Open the js/application.js and add:

var game = new stopwatch.Game(5);
    window.game = game;

The first line create a new game with a target of five seconds. The last line exposes it so we can inspect it in the console. Reload the StopWatch game in the browser and type game in the console. It should give you a representation of the game we just created.

Creating a GameView

Having a Game object is great, but what use is it to us if we can not view it? We will create a GameView for that purpose. We would like to show the target time in the game view so go ahead and add the following line to index.html, just below the h1-tag.

<div id="stopwatch"><label for="target">Target</label><span id="target" name="target">?</span></div>

This will create a spot for us to place the target time in. If you refresh the StopWatch game, you should see “Target: ?” in the window.

Just like we create a Game, we are going to create a GameView. Head over to js/stopwatch.js and add:

var GameView = stopwatch.GameView = function(game, container){
        this.game = game;
        this.container = container;
        this.update();
    };
    GameView.prototype.update = function(){
      var target = this.container.querySelector('#target');
        target.innerHTML = this.game.target;
    };

The GameView constructor accepts a Game object and a container to place the game in. It stores these arguments and then calls the update method. The update method searches within the container for the the tag with id target and writes the value of game.target into it.

Creating a GameView object

Now that we created a GameView we better hook it up to the game object we already created. Open js/application.js and change it to:

var game = new stopwatch.Game(5);
    new stopwatch.GameView(game, document.getElementById('stopwatch'))

This will create a GameView object with the game object and the div-tag we just created. If you refresh the StopWatch game the question mark will be substituted with the target time of 5000 milliseconds.

Show Current Time

Besides the target time, we would also want to show the current time, i.e. the time that is ticking away towards the target. This is quit similar to the target, with a slight twist. Instead of a property we are using a getter-method.

In index.html add a line for the current time.

<label for="current">Current</label><span id="current" name="current">?</span>

In js/stopwatch.js, right after the constructor add:

    Game.prototype.current = function(){
        return 0;
    };

Finally change the update-method of GameView to also update the current state.

var target = this.container.querySelector('#target');
        target.innerHTML = this.game.target;
        var current = this.container.querySelector('#current');
        current.innerHTML = this.game.current();

Refresh the StopWatch game to see the changes.

Starting & Stopping the Game

We would like the current time start ticking when we press the spacebar and stop ticking when we release the spacebar. For this we are going to create start and stop methods on the Game. We also need to keep track if the game is already started or stopped, so we start by initializing them in the constructor.

Change the Game constructor, found in the js/stopwatch.js, to initialize started and stopped properties.

this.target = 1000 * seconds; // in milliseconds
        this.started = false;
        this.stopped = false;

Next add a start and stop method that record that time when the game was started and stopped.

Game.prototype.start = function(){
        if (!this.started) {
            this.started = true;
            this.startTime = new Date().getTime();
            this.time = this.startTime;
        }
    };
    Game.prototype.stop = function(){
        if (!this.stopped) {
            this.stopped = true;
            this.stopTime = new Date().getTime();
        }
    }

At last, we can change the current method to use the start and stop times.

if (this.started) {
            return (this.stopped ? this.stopTime: this.time) - this.startTime;
        }
        return 0

If you now refresh the StopWatch game, we can test the functionality in the console tab. The follow excerpt demonstrates that

Ready to play
> game.start();
< undefined
> // wait a few seconds
> game.stop();
< undefined
> game.current();
< 7584 // depends on how long you wait

Update the GameView

You might have noticed that despite the current of the game changed, the GameView did not reflect this. I.e. after running the above excerpt, the StopWatch window still shows zero for the current time.

Let’s create a game loop that continously updates the view. In order to achieve this we need to assign the GameView object to a variable and update it inside the game loop. Change js/application.js accordingly:

var view = new stopwatch.GameView(game, document.getElementById('stopwatch'));

 function loop(){
        view.update();
        requestAnimationFrame(loop);
    }
    loop();

This uses the requestAnimationFramefunction to schedule the next run of the loop. If you now refresh the StopWatch game and rerun the excerpt above the current should be updated in the view.

Update the Game

Eventhough the GameView is updated when we start and stop the game, it still does not show the current time when it is ticking. Let’s remedy this.

The current-method of the Game is depending on the time property, but this is not updated. Create a tick-method on Game in js/stopwatch.js that updates the time property.

Game.prototype.tick = function(){
        this.time = new Date().getTime();
    };

and call it in the game loop in js/application.js.

function loop(){
        game.tick();
        view.update();
        requestAnimationFrame(loop);
    }

Refreshing the game and rerunning the excerpt will update the current time when it ticks.

Connect User Input

Manipulating the Game object is fine when checking that the game works, but it is not very useable. We will change that. The Game will process user input. It will start when the spacebar is pressed and will stop when the spacebar is released again.

We can make this happen by registering listeners for keydown and keyup events. When an event is triggered the listeners get called and can inspect the event and check what key was pressed, as demonstrated in the following code in js/application.js.

    document.body.addEventListener('keydown', function(event){
        if (event.keyCode == 32 /* space */) {
            game.start();
        }
    });
    document.body.addEventListener('keyup', function(event){
        if (event.keyCode == 32 /* space */) {
            game.stop();
        }
    });

Try this out in the browser and you will be able to control the game with the spacebar.

Birds Eye View

Lets take a step back and see what we have achieved and identify the key parts.

  1. Game state, we created a Game that is responsible for keeping track of all the details of the game.
  2. **Game View*, next we created a GameView that is responsible for presenting the Game to the player.
  3. Game Loop, the game loop continuoulsy triggers the Game View to render it self and it updates the Game.
  4. User Input like time, key presses or mouse movement, is transformed into Game controls that change the game state.

These are the four key ingredients to every game. Every game engine provides means to create, manipulate and manage all these aspect. Although there are local variations how game engines achieve this, it all drills down to this.

Summary

We took a peek under the hood of game engines by creating a game and identifying what is common to all games. I.e. game state to keep track of the game, game view to present to the players, user input to control the game and game loop to breath life into the game.

About the author

Daan van Berkel is an enthusiastic software craftsman with a knack for presenting technical details in a clear and concise manner. Driven by the desire for understanding complex matters, Daan is always on the lookout for innovative uses of software.

LEAVE A REPLY

Please enter your comment!
Please enter your name here