Tuesday, July 10, 2018

Helix Jump development tutorial using THREE.JS (Part 1)

Getting Started

Helix jump is a famous 3D bouncing ball game. Ball bounce through the helix tower and complete the levels.
I will use THREE.JS to develop Helix Jump game. Three.js is powerful JavaScript 3d library to develop games, models and its community is very active.

I will try to add all the basic features of this game in this tutorial.

This is what you will achieve after completing this tutorial.



Requirements

We will need some assets to develop this game.

1- Ball
2- Platform

Click here to download resources


Download the libraries

1- three.min.js
2- MTLLoader.js
3- OBJLoader.js


Presetting

Please follow these steps to configure project.

1- Create a folder and name it HelixJump
2- Create two files index.html and game.js and put it in HelixJump folder
3- Download the resource and extract in HelixJump folder
4- Download the libraries and put in HelixJump folder


Framework

Three.js is a cross-browser JavaScript library and Application Programming Interface (API) used to create and display animated 3D computer graphics in a web browser. Three.js uses WebGL. The source code is hosted in a repository on GitHub.

Alternatively you can use three.js cdn link in your game or download the file from that address and use it.


HTML

index.html

<!DOCTYPE html>
<html>

<head>
    <title>HelixDemo</title>
    <script type="text/javascript" src="three.min.js"></script>
    <script type="text/javascript" src="MTLLoader.js"></script>
    <script type="text/javascript" src="OBJLoader.js"></script>
</head>
<style>
#loader {
    width: 200px;
    height: 20px;
    background: #000;
    border: 2px solid #000;
}

#bar {
    width: 200px;
    height: 20px;
    background: red;
    border: none;
}

#scoreBoard {
    width: auto;
    height: 25px;
    font-weight: bold;
}
</style>

<body style="text-align:center;">
    <p id="scoreBoard">Score 0</p>
    <script type="text/javascript" src="game.js"></script>
</body>

</html>

In this index.html file i have added three libraries. I this game we will use Model and Objects to load some shapes like Platform and Ball.
To load model we need MTLLoader.js and Corresponding OBJLoader.js to load the Objects.
This model and object is exported form 3D shapes designing Tools like Blender and Maya.
We will use our game.js to write code which is loaded inside body.


JavaScript

Lets create a skeleton of our code. Add these lines in game.js
var Game = Game || {};
Game.init = function() {
}

window.onload = function() {
    Game.init();
};



Setting the Scene

We begin by creating a scene ( 3D world where every thing will be added )
Game.init = function() {
    .....
    this.scene = new THREE.Scene();
}

Camera!

Don't buy this one



Three comes with array of cameras such as PerspectiveCamera, OrthographicCamera, etc. For this part we will use PerspectiveCamera.

Our PerspectiveCamera accepts four arguments: fov, aspect, near, far

  • The fov (field of view is how much you can see around the camera's center.
  • The aspect is the ratio of width to height of a screen
  • near and far are the planes of a solid. near is the closest an object or part of an object can be to the camera while still being rendered. far is the furthest an object can be from the camera and still be rendered.

Here's an example of the PerspectiveCamera.

Game.init = function() {
    .....
    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera(90, 375 / 667, 0.1, 1000);

    //this.camera.position.set(x, y, z);
    this.camera.position.set(0, 2, -6);
    this.camera.lookAt(new THREE.Vector3(0, -2, 0));
}


Light

Add source of light so that world will lit and everything will be visible.
We will add two types of light

1- AmbientLight

This light globally illuminates all objects in the scene equally.
This light cannot be used to cast shadows as it does not have a direction.

2- PointLight

Its a light that gets emitted from a single point in all directions. A common use case for this is to replicate the light emitted from a bare lightbulb.

Game.addLights = function() {
    // LIGHTS
    var ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    this.scene.add(ambientLight);

    this.light = new THREE.PointLight(0xffffff, 1, 18);
    this.light.position.set(-3, 6, -3);
    this.light.castShadow = true;
    this.light.shadow.camera.near = 0.1;
    this.light.shadow.camera.far = 25;
    this.scene.add(this.light);
}

The WebGLRenderer

The next important thing is to create a WebGLRenderer. This is the piece that is responsible for the magic of displaying your creation. Basically it render everything which is displayed.
!! FEELING GOD !!

Game.init = function() {

        .....
        .....
        .....

    // antialias set to true to get smooth edges
    this.renderer = new THREE.WebGLRenderer({ antialias: true });
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(375, 667);
    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.type = THREE.BasicShadowMap;

    document.body.appendChild(this.renderer.domElement);
}


After the renderer is created append the dom element to document body.

Game Update

Everything is set up and now its time to update the world every 60 fps (Frame Per Second).
You can use requestAnimationFrame to update the scene and camera.
This update function will be call continuously every frame.


function update() {
    requestAnimationFrame(update);
    Game.renderer.render(Game.scene, Game.camera);
}

Adding Shapes

Now lets add a function loadResources which add some shapes to our game. For now lets add background which we will rotate it to 90 deg in X-axis.

Game.loadResources = function() {

    var bgMesh = new THREE.Mesh(
        new THREE.PlaneGeometry(100, 1000, 5, 5),
        new THREE.MeshBasicMaterial({
            color: 0xABB2B9,
            wireframe: false,
            side: THREE.DoubleSide
        })
    );

    bgMesh.rotation.x += Math.PI;
    bgMesh.receiveShadow = true;
    bgMesh.position.set(0, -1, 4);
    this.scene.add(bgMesh);

}
 
To create a plane we have to use THREE.Mesh
It receive two param geometry and material

Geometry is all about which type of shape you want to create.

Materials describe the appearance of objects. They are defined in a (mostly) renderer-independent way, so you don't have to rewrite materials if you decide to use a different renderer.

I have used THREE.MeshBasicMaterial because i don't want my background to be illuminated by light. Instead we can use THREE.MeshPhongMaterial

We have successfully created a background for our game.

Now lets add a cylinder to our game

Game.loadResources = function() {

    var bgMesh  = new THREE.Mesh(
        new THREE.PlaneGeometry(100, 1000, 5, 5),
        new THREE.MeshBasicMaterial({
            color: 0xABB2B9,
            wireframe: false,
            side: THREE.DoubleSide
        })
    );

    bgMesh.rotation.x += Math.PI;
    bgMesh.receiveShadow = true;
    bgMesh.position.set(0, -1, 4);
    this.scene.add(bgMesh);

    this.cylinder = new THREE.Mesh(
        new THREE.CylinderGeometry(1.5, 1.5, 150, 50),
        new THREE.MeshPhongMaterial({ wireframe: false, color: 0xEDBB99 })
    );
    this.cylinder.receiveShadow = true;
    this.cylinder.position.set(0, -75, 0);

    // cylinder group is added, all platforms will be added to it
    this.cylinderGroup = new THREE.Group();
    this.cylinderGroup.add(this.cylinder);
    this.scene.add(this.cylinderGroup);

}

Here a cylinderGroup is added, we will add all platforms to it so that all the platforms will be rotated along with cylinder

And call loadResources and update from Game.init

Game.init = function() {
    
    ..........
    ..........
    ..........

    this.renderer = new THREE.WebGLRenderer({ antialias: true });
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(375, 667);
    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.type = THREE.BasicShadowMap;

    document.body.appendChild(this.renderer.domElement);

    this.loadResources();
    update();
}



So far we have achieved this


Now lets add some objects to our game.

You may think if object are to be loaded then a preloader must be there to ensure everything is loaded then our game may continue. GOOD!!

So lets add a loader and create some constants and a resource loaded listener

Loading Manager

Game.addLoader = function() {

    var progress = document.createElement('div');
    progress.setAttribute("id", "loader");
    var progressBar = document.createElement('div');
    progressBar.setAttribute("id", "bar");
    progress.appendChild(progressBar);
    document.body.appendChild(progress);

    this.loadingManager = new THREE.LoadingManager();
    this.loadingManager.onProgress = function(item, loaded, total) {
        progressBar.style.width = (loaded / total * 100) + '%';
        console.log(item, loaded, total);
    };
    this.loadingManager.onLoad = function() {
        console.log("loaded all resources");
        !Game.GAME_LOADED && document.body.removeChild(progress);
        Game.GAME_LOADED = true;
        Game.GAME_STARTED = true;
        Game.onResourcesLoaded();
    };
}

Game.onResourcesLoaded = function() {

}

Game.USE_WIREFRAME = false; // create a constant so that we can toggle between wireframe
Game.GAME_LOADED = false;
Game.GAME_STARTED = false;

I hope this tutorial is enough to you continue with the development of rest of game?
JUST KIDDING.

Where to go from here? Part 2 of the tutorial


No comments:

Post a Comment

Note: Only a member of this blog may post a comment.