Toad Attack - part 1

Toad Attack - Build a HTML5 game with Babylon.js - part 1


Babylon.js is a 3D game engine based on WebGL and javascript, created by David Catuhe and David Rousset.
This engine is relatively new and less popular than Three.js, but many think it is easier to understand and easier to manipulate . Moreover, it is completely open source (with a very reactive community) and game oriented, with several features built specifically for games, like gamepad camera, hardware accelerated instances, and so on.
The complete list of feature is described on the Github page of the project.
With this framework, you can build games like this one, based on the popular licence Assasin's creed.

To understand this tutorial, you don't have to have any experience in game development. I will try to explain with many details of what is done and how to do it. However, having a small experience in Javascript is necessary.

How it works

It's the power of WebGL: thanks to a new HTML5 element (canvas), your browser can access to the computer GPU, allowing complex operations to be displayed, like 3D elements.
Babylon.js is a wrapper for all these operations (called a 3D engine), and the GPU can be used without doing anything from the developer !

What you will create

We will start with a very simple game describing many features of Babylon.js :

  • Getting ready with Babylon.js
  • Mesh creation and import
  • Textures and skyboxes
  • Handle keyboard events
  • Basic animations
The part 1 will cover the two first points. This game will be called TOAD ATTACK - click here to run the game, and will look like this :
It will be a very simple game, working like the famous Rock Band licence : few mushrooms will walk along 3 lanes, and the player will have to press the key corresponding to each lane to kill the mushroom on the red platform.
But first, let's start by installing the tools you will need !

The tools

Only three softwares are mandatory :

  • a text editor (Sublime Text, Notepad++, Atom, Visual Studio Web,...)
  • A web browser supporting WebGL ( a complete list here). The last version of Chrome/Firefox/IE is perfect :)
  • A web server to load all our game assets. You can either install WAMP for Windows (or MAMP for Mac), or use the public folder of your DropBox account.
The webserver is necessary because we will load several external resources in our game : textures, custom 3D models, ... All these resources are loaded via XHR (it's a common practice), and for security reasons XHR does not work offline (cross-origin requests not allowed). There is a way to work without a webserver, but I will explain it on another tutorial :)

The beginning

Once you choose your tools and your webserver is ready, create a folder toad_attack in your webserver directory. This folder will be your 'work' folder. Then, create these folders in your work directory :

  • assets - contains all our games assets
  • css - contains the css or our website
  • js - contains all our javascript code
Finally, you will have to download the last version of Babylon.js on the Github project page. Get the project zip, and extract the file babylon.1.12.js in the folder /toad_attack/js.Voila ! You're ready to code :)

The HTML page

The HTML page is really simple, as everything is done in Javascript. The only important element is <canvas>, which represents our game.

<!DOCTYPE html>
<head>
    <link rel="stylesheet" type="text/css" href="css/style.css" />
        <script src="js/babylon.1.12.js"></script>
        <script src="js/initScene.js"></script>
</head>
<body>
    <canvas id="renderCanvas"></canvas>
</body>
</html>
Save this in a file called index.html at the root of your work folder.

The CSS page

The css file is very simple too. Nothing fancy here, the canvas is just configured to take all the windows width and height.

html, body {
    height: 100%;
    width: 100%;
    padding: 0;
    margin: 0;
    overflow: hidden;
    position: fixed;
}
#renderCanvas {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
}
Save this code in a file css/style.css.

If you open the index.html page in your favorite browser, you should see nothing yet. Time to add a scene !

The Babylon.js magic

Creates a new file in the js folder called initScene.js. Open this file in your favorite text editor and copy-paste this code :

// Global variables
var canvas, engine, scene, camera, score = 0;
var TOAD_MODEL;

// An array to store each ending of the lane
var ENDINGS = [];

/**
* Load the scene when the canvas is fully loaded
*/
document.addEventListener("DOMContentLoaded", function () {
    if (BABYLON.Engine.isSupported()) {
        initScene();
    }
}, false);

/**
 * Creates a new BABYLON Engine and initialize the scene
 */
function initScene() {
    // Get canvas
    canvas = document.getElementById("renderCanvas");

    // Create babylon engine
    engine = new BABYLON.Engine(canvas, true);

    // Create scene
    scene = new BABYLON.Scene(engine);

    // Create the camera
    camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(0,4,-10), scene);
    camera.setTarget(new BABYLON.Vector3(0,0,10));
    camera.attachControl(canvas);

    // Create light
    var light = new BABYLON.PointLight("light", new BABYLON.Vector3(0,5,-5), scene);
}

What ?

This code is easy to read, but let's explain it step by step.

// Global variables
var canvas, engine, scene, camera, score = 0;
var TOAD_MODEL;
var ENDINGS = [];
var ENEMIES = [];
Here, several global variables are created. These variables will contain a reference to :
  • the canvas (our html5 game element),
  • the Babylon engine (responsible for drawing the game scene on the canvas)
  • the game scene (contains our game elements)
  • the game camera.
  • the score
A game can contains several different scenes (for example a scene of a city with a lot of buildings, and a scene of the inside of one building), but only one engine is necessary to display your game.
Same, several cameras can be created to display several point of views. In our game, we only need one.
The array ENDING will contains an object representing the end of the lane.

/**
* Load the scene when the canvas is fully loaded
*/
document.addEventListener("DOMContentLoaded", function () {
    if (BABYLON.Engine.isSupported()) {
        initScene();
    }
}, false);
The event DOMContentLoaded is fired by the browser when the HTML is loaded. We need this in order to have a fully loaded canvas element to create the Babylon engine. If you forget it, you will have the error Uncaught Error: WebGL not supported .
In the callback function, we are testing if a Babylon engine can be created with the client browser (actually if the browser supports WebGL). In this case, the method initScene is called.

// Get canvas
canvas = document.getElementById("renderCanvas");

// Create babylon engine
engine = new BABYLON.Engine(canvas, true);
The canvas element is retrieved and a Babylon engine is created for this canvas. The second parameter true is to activate the game antialiasing.

// Create scene
scene = new BABYLON.Scene(engine);
A scene is created (and will be populated later).

// Create the camera
camera = new BABYLON.FreeCamera("camera", new BABYLON.Vector3(0,4,-10), scene);
camera.setTarget(new BABYLON.Vector3(0,0,10));
camera.attachControl(canvas);
A camera is created (to 'see' our scene) at the position (0,4,-10) and its target is set to the point (0,0,10).
There are several types of camera in Babylon.js. A FreeCamera is the camera available in most of FPS games: controlled with keyboard arrows and the mouse. These controls are activated by the function attackControl().
A comprehensive list of cameras is described here.

// Create light
var light = new BABYLON.PointLight("light", new BABYLON.Vector3(0,5,-5), scene);
A light is mandatory in our scene to actually see something. If you forget the light, all objects will be dark.
Such as cameras, there are several types of camera in Babylon. A comprehensive list of lights is described here.

If you try to open index.html in your browser, you will see... nothing. And it's totally normal.

I want to see dammit !

Relax, here it comes :) What I didn't tell you is Babylon needs one last function in order to display anything : the render function.
Add this function at the end of the initScene function and run index.html in your browser :

engine.runRenderLoop(function () {
    scene.render();
}

Yeah, purple everywhere !

You should see a purple screen, with nothing inside. It's ok, because we didn't created anything in our scene !
The render function is a function called by the 3D engine to draw our scene 60 times by seconds. Each render call draws a frame, so our game will run at 60 frames per seconds (60 FPS). The more code you will add in this function, the more time a frame will be displayed, and lags could be appears.

A simple sphere

Now that you understand the basics of a 3D game (a scene, a camera, a light and a render function), let's create a simple object : a sphere. Babylon makes it super easy with only one line of code. Create a new function called initGame :

/**
 * Initialize the game
 */
function initGame() {
    BABYLON.Mesh.CreateSphere("sphere", 10, 1, scene);
}
And updates the loadGame function to launch initGame by adding :
initGame();
Run index.html in your browser, and you should see a sphere in the center of your screen. You can even move the camera with keyboard arrows.

Babylon can create several basic elements, such as Box, Torus, planes, ... As an exercise, you can try to create all these simple objects in your scene.

We need to go deeper

Clean your function initGame and replace it with this :

function initGame() {

    // Number of lanes
    var LANE_NUMBER = 3;
    // Space between lanes
    var LANE_INTERVAL = 5;
    var LANES_POSITIONS = [];

    // Function to create lanes
    var createLane = function (id, position) {
        var lane = BABYLON.Mesh.CreateBox("lane"+id, 1, scene);
        lane.scaling.y = 0.1;
        lane.scaling.x = 3;
        lane.scaling.z = 800;
        lane.position.x = position;
        lane.position.z = lane.scaling.z/2-200;
    };

    var createEnding = function (id, position) {
        var ending = BABYLON.Mesh.CreateGround(id, 3, 4, 1, scene);
        ending.position.x = position;
        ending.position.y = 0.1;
        ending.position.z = 1;
        var mat = new BABYLON.StandardMaterial("endingMat", scene);
        mat.diffuseColor = new BABYLON.Color3(0.8,0.2,0.2);
        ending.material = mat;
        return ending;
    };

    var currentLanePosition = LANE_INTERVAL * -1 * (LANE_NUMBER/2);
    for (var i = 0; i<LANE_NUMBER; i++){
        LANES_POSITIONS[i] = currentLanePosition;
        createLane(i, currentLanePosition);
        var e = createEnding(i, currentLanePosition);
        ENDINGS.push(e);
        currentLanePosition += LANE_INTERVAL;
    }

    // Adjust camera position
    camera.position.x = LANES_POSITIONS[Math.floor(LANE_NUMBER/2)];
}
Here, two functions are created :
  • The function createLane creates a box scales by 800 along the z-axis. This function will be called 3 times (for each lane).
  • The function createEnding creates a red plane. The code creating the red material is this one :
    var mat = new BABYLON.StandardMaterial("endingMat", scene);
    mat.diffuseColor = new BABYLON.Color3(0.8,0.2,0.2);
    
These two functions are then called in a for loop 3 times.
Finally, the camera position is adjusted to be at the top of the middle lane. Here is what should be displayed :
By the way, if you want to update the lane positions (or shapes), you should know that Babylon.js is a left-handed coordinate system : the z-axis is in front of you, y-axis on the top and x-axis on the right.
Now that our scene is up and ready, let's add some mushrooms !