Unity WebGL games

In Game Development


Creating a game for WebGL using Unity 5 and JavaScript
alt
Editorial Commitee Qualified.One,
Management
alt
"

Unity is a cross-platform game engine that makes it easy to create games for PCs, consoles, mobile devices, and websites. The latest version of the engine (Unity 5) features WebGL exporting, so developers can easily publish their games on the web. As the name suggests, this exporter uses WebGL - JavaScript API for rendering interactive 3D computer graphics - as well as asm.js - a subset of JavaScript which was developed by Mozilla and touted as "an assembly language for the web". More information on Asm.js and WebGL for Unity and Unreal Engine is available here.

In this tutorial I want to show you how to set up a Unity experience. I will also demonstrate how to create a simple game in Unity with JavaScript and export it for the web.

At the end of the tutorial you'll have a game like this (you'll need a WebGL-enabled browser to view it). The project is also available for download from GitHub repository.

So, let's get started.

A few words about JavaScript in Unity

When we talk about JavaScript in Unity, we mean a kind of JS dialect called UnityScript. And while the Unity experts themselves call this language JavaScript, more skeptical internet users believe that the JavaScript support in Unity is a marketing ploy. At any rate, we should make a disclaimer at the outset, that UnityScript does not conform to the ECMAScript specification, and no one is even making an attempt to resolve these inconsistencies.

Installing Unity

First we need a working version of Unity, you can download it here. The installation files are available for Windows and for Mac OS X. Linux users can run Unity with Wine or use another convenient method.

After installation we can get started. Open Unity and create a new 3D project.

Set up the project

Now that it's open, let's take a quick walk through the basic interface:

  • On the left side is the Hierarchy panel. It shows all elements of the current scene. The scene is the visual representation of the game (level or menu). There are currently two items in this panel: Main Camera and Directional Light.
  • The Scene panel in the middle displays the camera and light in 3D space.
  • Next to Scene is the Game tab. This shows how the player will see the game. This is needed to test the game in the editor.
  • On the right side there is the Inspector panel where you can change the element settings. Let's see how it works. First let's select Directional Light in the Hierarchy panel. We will see a lot of information about this type of light and we can disable shadows in it by selecting Shadow Type: No Shadows.
  • At the bottom of the screen is a Project window showing the files we need to create for the game.

There is one more thing to do before you start: save the current scene. The File > Save Scene option opens the Save Scene dialog, leading to the Assets folder. The most common way to organise files in Unity is to use subfolders. So let's add a new subfolder Scenes to the Assets folder and save the current scene there, naming it Level.unity.

Creating the hero

The hero in our game will jump up from one platform to another. If he doesn't jump up on one of them in time, he will fall into an abyss and lose. We will start by creating the hero. Since the game will be first-person, the appearance of the hero is irrelevant and we can use a standard sphere instead. The plus side is that the sphere is quick to create, and its physical characteristics are great for jumping on platforms. Let's add it by selecting Create in the Hierarchy panel and editing the following parameters on the Inspector tab:

Position { X: 0, Y: 2.5, Z: 0 }
Scale { X: 0.3, Y: 0.3, Z: 0.3 }

Press Play to check the result. A 3D space should appear on the screen with a sphere against the horizon.

It has to have a weight in order for the sphere to fall. We need to add a new component to it by clicking the Add Component button in the Inspector panel and selecting Rigidbody. Since we don't want the sphere to rotate, we need to fix its position with the Rigidbody component. To do this, open Constraints and select all axes in the Rotation line. Play the scene again and you will see that the sphere is now falling.

To prevent the sphere from falling endlessly, let's create some sort of platform. To do that, add a flat cube with a Scale.Y value of 0.1. Play the scene again and see if the sphere successfully lands on the platform. But, it's worth noting, all of this doesn't look very natural. So how do we make the sphere bounce? For that we need some physical materials.

Let's give physical properties to the sphere

First of all, let's create a physical material for our sphere that will make it bounce from the contact surface. To do this, we need to create a new subfolder called Materials in the Assets folder. Inside this subfolder, let's create a new physical material and name it Bouncy_Sphere. Here are the values we need to specify in the Inspector panel:

Dynamic Friction: 10
Static Friction: 10
Bounciness: 1
Friction Combine: Maximum
Bounce Combine: Maximum

If we add this material to the Sphere Collider, the sphere will bounce, but always at the same height. In order for it to bounce higher each time, we need to add a physical material to the platform as well. Let's create another material called Bouncy_Platform and apply the following values to it:

Dynamic Friction: 0.9
Static Friction: 0.9
Bounciness: 1
Friction Combine: Average
Bounce Combine: Multiply

To avoid confusion, let's rename our flat platform cube to Platform by clicking on it twice in the Hierarchy panel. Now, when you start the game, you'll see that the sphere bounces higher each time.

Let's also create a new standard material called Platform, so that we can give the platform some colour. Once created, paste the colour #C8FF00 in front of the Albedo value, then drag this material onto the platform element. The platform should now turn yellow.

Adding a First Person View

To do this, in the Hierarchy panel, drag the camera onto the sphere. As a result, the camera will become a child element of the sphere and will move with the sphere. You also need to set some additional options for the camera:

Position { X: 0, Y: 1, Z: 0 }
Rotation { X: 90, Y: 0, Z: 0 }
Scale { X: 2.5, Y: 2.5, Z: 2.5 }
Clear Flags: Solid Color
Background: #000
Field of View: 80.3

Additionally, we add a lantern as a second child element to the sphere. It will help the player have an idea of the sphere's jump height at any point in the game. The lantern parameters are as follows:

Rotation { X:90, Y:0, Z:0 }

Configure the controls in the game

Our goal is to use the mouse or trackpad to allow the player to move the sphere in a certain direction. To this end, we'll write the first script. As with Rigidbody, the script is added to the game element as a component. In our example we will add a JS script called InputController to the camera. Just as we did with the scene and materials, create a new folder in the Project panel called Scripts, which will contain our script. Double-clicking on the new script will open the standard Unity editor MonoDevelop. You can replace it with any other editor (Unity > Preferences > External Tools), but it doesn't matter right now.

As you can see there is already some code in the script. First of all, let's create some variables under the first line with the text #pragma strict (enables forced type definition in Unity):


#pragma strict

public var Hero : GameObject;
private var halfScreenWidth : float;
private var  halfScreenHeight : float;

function Start () {}

function Update () {}

The first is a public variable belonging to the GameObject type. It refers to the sphere. Let's return to Unity, still leaving the camera selected. We'll see that this public variable is located next to an empty input field. Let's move the sphere to this area, thus assigning a value to this variable.

The other two variables are private, and will be assigned values in the Start function. This function is called only once, after the scene starts. Both private variables will be assigned half the screen width and half the screen height, respectively. To do this, we use the built-in Screen class:


function Start () {
  halfScreenWidth = Screen.width / 2;
  halfScreenHeight = Screen.height / 2;
}

The only thing left to implement in the InputController script is to get data about mouse position and movement. To do this, we'll use the Update function, which is called for each frame:


function Update () {
  var x : float = 0.0;
  var z : float = 0.0;

  x = ( Input.mousePosition.x - halfScreenWidth ) / halfScreenWidth;
  z = ( Input.mousePosition.y - halfScreenHeight ) / halfScreenHeight;

  Hero.GetComponent( HeroController ).SetPosition( x, z );
}

Each of the two new variables x and z denote a corresponding axis. When we look along the y-axis, we see the horizontal x-axis and the vertical z-axis. We will change the position of the sphere on these axes depending on the data received from the mouse. We'll need a static variable Input.mousePosition that returns a two-dimensional vector. A vector whose zero value falls on the bottom left corner should move to the middle of the screen in our coordinate system. The following code snippet demonstrates this coordinate transformation. Finally, let's call the setHeroPosition function with both of the calculated values as arguments. We will write this function in a new HeroController script attached to the sphere:


#pragma strict

public function SetPosition ( x : float, z : float ) {
  transform.position.x = x;
  transform.position.z = z;
}

Let's test how our code works by moving the mouse or trackpad so that the sphere falls off the platform.

Implement procedural creation of platforms

To automatically create platforms, we need something like a platform template. In Unity, such templates are called prefabs. To create a prefab, you need to drag and drop a platform from the Hierarchy panel into a new subfolder of the Prefabs folder in the assets folder. Prefabs are easily recognisable in the Hierarchy panel by their blue colour. All platforms except the first will be created using the new GameManager script linked to the camera. First, in the script we will address the necessary variables:


#pragma strict

public var Platform : GameObject;
public var Hero : GameObject;

private var boundary : float;
private var rotation: Quaternion;
private var lastPlatformPosition : Vector3;

function Start () {
  boundary = 1.0;
  rotation = Quaternion.identity;
  lastPlatformPosition = new Vector3( 0, 0, 0 );
}

function Update () {}

We need to refer to the prefab panel and sphere, so we need to drag and drop them to the appropriate areas of the editor. We also create three private variables that will be used to create an instance of the prefab panel.

  • The boundary variable defines the boundary of the y-axis. Each time the hero jumps above this boundary, a new panel should be created.
  • The second variable is responsible for the rotation needed to create a new prefab instance. The value of Quaternion.identity cancels the rotation, as we need it.
  • The lastPlatformPosition variable stores the position of the last platform as a 3D vector.

Now let's make it so that each frame checks if the sphere is above the given boundary. If yes, the boundary will be raised and a new panel instance will be created:


function Update () {
  if ( Hero.transform.position.y > boundary ) {
    var position : Vector3;

    boundary += 1.0;
    position = getNextPlatformPosition();
    Instantiate( Platform, position, rotation );
  }
}

Then add code to get the position of the next panel. Put this code in an additional function to preserve overall readability:


private function getNextPlatformPosition () {
  var position : Vector3;

  do {
    position = new Vector3( Random.Range( -1, 2 ), boundary, Random.Range( -1, 2 ) );
  } while ( position.x == lastPlatformPosition && position.z == lastPlatformPosition );

  lastPlatformPosition = position;

  return position;
}

To avoid duplicating x and z values in the new panel with respect to the previous one, use the do while loop. The Unity Random.Range function will help us get arbitrary values of x and z axes. In any case, we want their range to be between -1 and 2. Finally, we save the new panel position as the last one and return it.

Adding the game menu

At this stage the player can jump on and off the platforms by moving the mouse in the desired direction. But if he misses, he will fall down infinitely. This needs to be corrected. From now on, if the sphere falls below the first platform, a new stage will appear on the screen.

First of all, we need to check if the sphere has fallen below a certain point. To do so, edit the if statement of the update function in the GameManager script. The else if statement will check if the sphere position is below -2.0. If so, it will call the closed gameOver function:


function Update () {
  if ( Hero.transform.position.y > boundary ) {
    var position : Vector3;

    boundary += 1.0;
    position = getNextPlatformPosition();
    Instantiate( Platform, position, rotation );
  } else if (Hero.transform.position.y < -2.0) {
    gameOver();
  }
}

We will use a new feature to track the status of the game:


private function gameOver () {
  Application.LoadLevel( 'Menu' );
}

In this case, we use the Application class, which allows using the LoadLevel method to load a new Menu scene. To do this, we first create a scene by selecting File > New Scene and save it as Menu. Then we need to add both scenes to the build process. The build settings are available in the File > Build Settings tab. Without closing the menu scene, click Add Current and add the scene to the build settings. Repeat the same action with the open level scene. You will now have the newly created menu scene on your screen when the game ends.

Add a button to start the game

If you want to play a game, you'll need to add a button to start it. So let's go back to the game menu scene settings and change first of all the camera settings in the Inspector panel:

Clear Flags: Solid Color
Background: #000
Width: 200
Height: 60

To add a button, we'll use the Unity UI elements that can be added as 3D elements through the Hierarchy panel. After adding an interface button, the following elements should appear in the Hierarchy: EventSystem and Canvas along with its child element Button and its child element Text.

Canvas is a container for all interface elements, it can be made somewhat adaptive. To do this, we need to switch Canvas Scaler: UI Scale Mode setting in Inspector panel from Constant Pixel Size to Scale With Screen Size. Now we can change button's position:

Rect Transform { Pos X: 0, Pos Y: 0, Pos Z: 0 }
Rect Transform { Width: 200, Height: 60 }

By removing the original image of the button and setting its colour to #C8FF00, we'll give the menu a slightly more decent look. Now let's change the text in the Text element to PLAY PREJUMP and set the font to 16 point. To make the button work, we'll use the new function which we'll add to the new UIController script for the Button element. The script consists of just one function which loads the level scene:


public function StartGame () {
  Application.LoadLevel( 'Level' );
}

This function can be applied in the button options in the Inspector panel. In the Button (Script) component settings, we can make the function be executed when the user clicks this component. To this end, we'll add a new function to the On Click () event by clicking the + icon. Now we can drag the button itself onto the input box. Then we will select the function just written from the UIController script (UIController.StartGame).

Publish your project as a browser-based game for WebGL

With Unity you can export your project as a WebGL application. Open the build settings and select WebGL as the platform. Then confirm your selection by clicking the Switch Platform button. After that, all that's left to do is click Build and select a name for the game. Once the build is complete, open the html file with any browser that supports WebGL.

Next steps

Of course our little game can be improved. For example, add scoring, different types of platforms, additional input methods, sounds and so on. The main thing we've seen in this tutorial is that the cross-platform Unity game engine provides a good combination of a WYSIWYG editor and scripting capabilities created in a JavaScript-like language. Depending on the specific requirements of your project, Unity can be a worthy alternative to WebGL frameworks.

Do you use Unity in your projects? 

"