Getting Started with BabylonJS

Isaias Pomales
9 min readJun 24, 2018

--

(a javascript framework for building 3d web browser games)

Babylon is a javascript framework that is built above the Web Graphics Library (WebGL). WebGL API is used for rendering graphics within a web browser. BabylonJS takes away a lot of the complexity involved with programming in WebGL.

We will be building a 3d roll-a-ball game to demonstrate the main components of the BabylonJS game engine. The goal of the game is to collect all the pieces scattered across the ground by rolling a ball. Play the game at the link below.

In this tutorial, we will begin by learning how to initiate a scene and set up the camera/lighting. We will then create basic elements that will then be animated and controlled through user input. Finally we will apply physics and collisions that will allow us to actually play the game. Let’s dive right in and initialize our project.

(At the time of writing this, I was using node version 8.11.2 and npm 5.6.0)

All of the source is here for your reference.

Initial Setup

npm init --yes
npm install --save babylonjs cannon express path
npm install --save-dev nodemon webpack webpack-cli

These are some of the packages I will be using in this project.

  • BabylonJS — Javascript Framework for creating 3d games for the browser.
  • Cannon — Physics Engine
  • Express — For Serving Assets (necessary for importing textures and models)
  • Webpack — For bundling all the game classes into one file.

Here is the directory architecture of the project.

/public
-index.html
-main.css
/src
/classes
index.js
server.js
package.json
webpack.config.js

The html file must contain a canvas tag where the game will actually be loaded.

public/index.html

This css makes the canvas take up the entire browser window.

public/main.css

The src folder is where all our BabylonJS classes will be defined. For now, we’ll just have an event listener for when the DOM is finished loading. This is where we will load the game onto the canvas in the html file.

src/index.js

Here’s the basic webpack configuration.

webpack.config.js

Here is a basic server setup. You could also just use a simple http server.

Game Initialization

Inside the game class is where we will initialize and set up the game. Create a new file for the game class inside the classes folder. Any other classes will also be created in this folder.

touch src/classes/Game.js

At the bare minimum, the game needs to have a few components.

  • Engine — The object that interacts directly with the WebGL API
const canvas = document.getElementById(canvasId);
this.engine = new BABYLON.Engine(canvas, true);
  • Scene — The container which holds all the objects to be rendered
this.scene = new BABYLON.Scene(this.engine);
  • Camera — This is needed to display the scene. There are various types. I chose an Arc Rotate Camera which orbits around a given a radius and alpha/beta angles. You can control the camera with either the mouse pad or arrow keys.
this.camera = new BABYLON.ArcRotateCamera(
'camera',
Math.PI / 2,
Math.PI / 3,
35,
BABYLON.Vector3.Zero(),
this.scene
);
  • RenderLoop — Here is where the scene is rendered 60 times per second.
this.engine.runRenderLoop(() => {
this.scene.render();
})
src/classes/Game.js

Structure of a BabylonJS Game

The data structure of the entire game is actually a collection of nodes in a graph where the root node is the scene. Every game object is a descendent of the scene. The coordinate system at the origin is as follows.

  • Positive Z-axis points forward
  • Positive Y-axis points upward
  • Positive X-axis points right
image via: https://www.programering.com/a/MDO4cDMwATA.html

This is known as left hand coordinate system because if you put your left hand thumb on the x axis and index finger on the y axis, you can point on the z axis with your middle finger.

A vector is used to define position/rotation, and can be created as follows

const vector = new BABYLON.Vector3(x, y, z).

We can talk about these transformations in either the world space or local space. All objects in the game are within the world space. Transformations on a specific object would refer to the local space of that object.

Here’s what the app should look at at this moment.

Game Objects

The Ground

Next we are going to create objects that will be added to this scene. In this tutorial I’m going to follow the pattern of creating a class for each object in the game. In this game, the main objects are listed below.

  • Ground — this will be the board of the game.
  • Piece — cubes that the player must pick up to win.
  • Player — the ball that a player can control

Let’s begin by making a generic game object that all other objects will extend. This is a common practice in game development. Every object will be created from the BABYLON.Mesh class.

src/classes/GameObject.js

Now we will define a ground class. The constructor function is going to create a ground with walls given a size. All of the walls are connected to the ground by setting their parent to be the ground. This allows for when we change position or rotation of the ground, that the changes apply to the children as well. We then set a material to the ground. Materials allow us to set the color or texture. In this tutorial, we’ll only set the color. Here are the possible colors we can set to an object.

  • Diffuse Color — The color that is reflected under light.
  • Specular Color — The color of the bright spot on shiny objects.
  • Ambient Color — Secondary diffuse color that is dependent on the scene’s ambient color. This color must be set on the scene before we can set it on an object within the scene.
  • Emissive Color — This is a color that an object can emit without any light source.

Below is the source file for the Ground Class.

src/classes/Ground.js

Add the ground to the scene by adding these lines somewhere in the Game class constructor before running the render loop.

this.ground = new Ground(GROUND_SIZE, this);this.ground.rotation.x = Math.PI / 2;

For setting the ambient color, this line below should be added after setting up the scene.

this.scene.ambientColor = BABYLON.Color3.White();
Here is what you should see at this point.

The Pieces

Now we are going to define a cube class for pieces a player will collect. This will be a simple Box Mesh. You can remove the original cube we created, it won’t be needed. Below is the code, but feel free to experiment with different colors and materials!

Below is a function that will be called in the Game Class. Given a number of cubes and board size, it will generate pieces and randomly place them on the board. The positioning and rotations are based off the location of the board.

The Player

The player will be a simple sphere mesh. Below is the class definition. I added special properties to its material known as Fresnel Parameters. There are properties that affect how light is reflected depending on the viewing angle.

A Fresnel Parameter is defined by the following properties:

  • leftColor — defines color on the edges
  • rightColor — defines at the center
  • bias and power — determine how the fresnel is computed
Here is what it should look like at this point.

Add these lines to the Game Class constructor to put the player on the board.

this.player = new Player(1, this);this.player.position = new BABYLON.Vector3(0, 1, 0);

Adding Shadows & Animations

In order to add shadows to our game, we’ll need to define a directional light. This can be created during the initial set up of the game alongside the hemispheric light.

this.dirLight.position = new BABYLON.Vector3(0, 20, 0);
this.shadows = new BABYLON.ShadowGenerator(1024, this.dirLight);this.shadows.useBlurExponentialShadowMap = true;
this.shadows.setTransparencyShadow(true);

Add this line to the Ground class so it can receive the shadows produced by GameObjects.

this.receiveShadows = true;

Now in the Player and Cube classes, add this line so that these GameObjects can cast shadows.

game.shadows.getShadowMap().renderList.push(this);

Next we will be animating our cubes so that they are always rotating about their y axis.

We do this by creating an animation object given a set of key frames. Every frame defines a value of the property. In this case the property is the y rotation. Finally, we start the animation specifying the start frame, end frame, and speed. Add these lines to the Cube class.

const animation = new BABYLON.Animation('animCube', 'rotation.y', 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);const keys = [  {    frame: 0,    value: 0  },  {    frame: 30,    value: Math.PI / 2  },  {    frame: 60,    value: Math.PI  },  {    frame: 90,    value: 3 * Math.PI / 2  },  {    frame: 120,    value: 2 * Math.PI  }];animation.setKeys(keys);
this.animations.push(animation);
game.scene.beginAnimation(this, 0, 120, true, 1.0);
Game board with shadows and cubes spinning about their y-axis.

Physics and User Interaction

Now we will initialize a physics engine that will allow the ball to roll. Add this line somewhere in the Game class after the scene is created.

this.scene.enablePhysics(new BABYLON.Vector3(0, -9.8, 0), new BABYLON.CannonJSPlugin());

In order for the ball to interact with the physics world, it needs a rigid body also known as an impostor. This impostor is where collisions and physics calculations are performed.

this.physicsImpostor = new BABYLON.PhysicsImpostor(  this,  BABYLON.PhysicsImpostor.SphereImpostor,  {    mass: 1,    friction: 0.9,    restitution: 0.9  },  game.scene);

The ground and walls will also need an imposter so that the ball wouldn’t fall through the ground. It’s mass will be set to zero in order to make it a static rigid body. Restitution sets how bouncy objects will collide.

this.physicsImpostor = new BABYLON.PhysicsImpostor(  this,  BABYLON.PhysicsImpostor.BoxImpostor,  {    mass: 0,    restitution: 0.9  },  game.scene);

Control User

In order to allow the user to control the ball, we will add event listeners to the Player Class. Movement is going to be controlled by adding a force to the ball in the direction set the keys pressed. I will be using WASD configuration for movement.

this.direction = BABYLON.Vector3.Zero();// user inputwindow.addEventListener(‘keydown’, event => {  switch (event.keyCode) {    case 87: // W      this.direction = new BABYLON.Vector3(0, 0, -1);      break;    case 65: // A      this.direction = new BABYLON.Vector3(1, 0, 0);      break;    case 83: // S      this.direction = new BABYLON.Vector3(0, 0, 1);      break;    case 68: // D      this.direction = new BABYLON.Vector3(-1, 0, 0);      break;    default:  }})window.addEventListener(‘keyup’, event => {  this.direction = BABYLON.Vector3.Zero();})

Now you’re able to roll the ball!

Collisions

Now let’s try to pick those cubes up! We’ll do this by utilizing the collision engine. Let’s add a function that will be called before rendering the next frame. Here, we can check if any of the cubes collided with the player. If there is a collision, that cube will be disposed and removed from the cubes array. Finally, we can add some finishing touches with some good old fashion DOM manipulation.

Add this <p> tag in the html file before the <canvas> tag.

<p id="info">Cubes left: </p>

Css styling for the info tag;

#info {  position: absolute;  background-color: #a0c4ff;  padding: 2em;  margin: 1.5em 1em;  font-family: sans-serif;  font-weight: bold;  font-size: 20px;  box-shadow: 1px 2px 15px #777;}

Add this before the render loop function in the Game Class.

this.scene.registerBeforeRender(() => {  let idx;  this.cubes.forEach(((cube, i) => {    if (cube.intersectsMesh(this.player)) {    cube.dispose();    idx = i;    }  }));  if (idx !== undefined) this.cubes.splice(idx, 1);  document.getElementById('info').innerText = `Cubes left: ${    this.cubes.length  }`;});
the end result

Now you’re able to collect cubes and see how many are left to collect. We covered a lot of the fundamentals of BabylonJS, but we barely scratched the surface! If you enjoyed this tutorial, please take a deeper dive in the official documentation. There is also a playground site where you can experiment and play around with code and run it right in the browser. Have fun!

--

--