How to Make A Simple HTML5 Game With Enchant.js

This is a post by Tutorial Team member Guts Rodsavas, an iOS development trainer at Software Park Thailand and game developer at Coffee Dog Games. Are you curious about developing cross-platform mobile games that work in a web browser? Well, as you probably know, Apple doesn’t allow Flash to run on iOS devices, and Adobe […] By .

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 7 of this article. Click here to view the first page.

Touch Detection

Now that you’ve got the penguin flapping, it’s time to get him responding to player touches. In order to do this, you will divide the screen into three vertical sections. When the player clicks or touches on any section, the penguin will quickly move to that section of the screen.

Touch/click detection in enchant.js is as simple as listening for touch events. Add the following code to the end of the SceneGame constructor:

// Touch listener
this.addEventListener(Event.TOUCH_START,this.handleTouchControl);

TOUCH_START is one of the touch events you can detect. The three available touch events are:

  • TOUCH_START: fires when the mouse button is clicked or a finger touches the screen.
  • TOUCH_MOVE: keeps firing as long as the player drags a mouse while pressing the button, or moves a finger that’s continually touching the screen.
  • TOUCH_END: fires once the player releases the mouse button, or lifts the finger off the screen.

Since you listen for TOUCH_START, handleTouchControl will be called as soon as the player touches the screen.

Add handleTouchControl to the SceneGame class (don’t forget the comma after the previous method):

handleTouchControl: function (evt) {
    var laneWidth, lane;
    laneWidth = 320/3;
    lane = Math.floor(evt.x/laneWidth);
    lane = Math.max(Math.min(2,lane),0);
    this.penguin.switchToLaneNumber(lane);
}

You divide the screen’s width by 3 to get the width of each section/lane.

The evt value for a touch event contains information about the touch position, and you can access it via x and y properties. Then you use this information to find the lane number. After you’ve determined which lane was clicked/touched, you tell the penguin to move to that lane.

Add the switchToLaneNumber method to the Penguin class (note that the new method goes in Penguin and not SceneGame):

switchToLaneNumber: function(lane){     
    var targetX = 160 - this.width/2 + (lane-1)*90;
    this.x = targetX;
}

There’s nothing complicated here – you just calculate the x position for the penguin, given the lane number.

Save main.js and refresh the browser again. Now try clicking on the screen, and you should see the penguin move to the lane you selected.

Penguin switching lanes

He’s getting ready for the Polar Olympics!

Ice, Ice Baby

Your penguin can now swim about, but if this is to be a proper game, there should be some obstacles for the penguin to dodge. It’s time you added some – don’t let the penguin off easy!

Start by adding an Ice class to the end of main.js:

// Ice Boulder
var Ice = Class.create(Sprite, {
    // The obstacle that the penguin must avoid
    initialize: function(lane) {
        // Call superclass constructor
        Sprite.apply(this,[48, 49]);
        this.image  = Game.instance.assets['res/Ice.png'];      
        this.rotationSpeed = 0;
        this.setLane(lane);
        this.addEventListener(Event.ENTER_FRAME, this.update);
    }
});

The Ice class is also a subclass of the Sprite class. The Ice constructor takes an argument indicating the lane where it will appear.

Notice that your Ice class is missing two methods. The first one is setLane, which you will use to set the object’s position according to lane number. The second is update, which you call every time ENTER_FRAME occurs.

Add setLane to the Ice class as follows:

setLane: function(lane) {
	var game, distance;
	game = Game.instance;        
	distance = 90;

	this.rotationSpeed = Math.random() * 100 - 50;

    this.x = game.width/2 - this.width/2 + (lane - 1) * distance;
    this.y = -this.height;    
    this.rotation = Math.floor( Math.random() * 360 );    
}

It’s a straightforward method. It calculates a random rotation speed for the Ice, which you will use to animate the ice in update method. Then the Sprite’s position is set, based on the lane.

One method down, one more to go! Add the update method as follows:

update: function(evt) { 
	var ySpeed, game;

	game = Game.instance;
    ySpeed = 300;
    
	this.y += ySpeed * evt.elapsed * 0.001;
	this.rotation += this.rotationSpeed * evt.elapsed * 0.001;           
    if (this.y > game.height) {
        this.parentNode.removeChild(this);        
    }
}

Once again, this is pretty straightforward. You keep adding speed to the ice’s y position so that it’ll move from the top of the screen to the bottom. You also use rotationSpeed to keep the ice boulder rotating.

Once the ice moves beyond the bottom of the screen, it is removed from the parent node, effectively removing it from the scene.

You can access a parent node of any node through the parentNode property. With it, you can tell the ice’s parent node to remove the ice from the tree structure once it moves beyond the bottom of the screen. Remember the node and scene relationship? If you add the ice to the scene, the scene becomes the parent node of the ice.

You’re using a resource, res/Ice.png, that you haven’t told the game to preload. So let’s go back to the beginning of main.js and add it to the preload list.

Modify the preload line to look like this:

game.preload('res/BG.png',
             'res/penguinSheet.png',
             'res/Ice.png');

And with this, your Ice class is frozen solid and ready to deploy. Why don’t you test it by making an ice boulder appear every three seconds!

To do this, go to SceneGame constructor. Add the following line after the TOUCH_START event listener line:

// Update
this.addEventListener(Event.ENTER_FRAME, this.update);

Once again, you tell the scene to listen for ENTER_FRAME, and report the event to the update method.

You need a timer to know when to generate an ice boulder. Add this to the end of SceneGame’s constructor:

// Instance variables
this.generateIceTimer = 0;

Add the update method to SceneGame as follows:

update: function(evt) {
    // Check if it's time to create a new set of obstacles
    this.generateIceTimer += evt.elapsed * 0.001;
    if (this.generateIceTimer >= 0.5) {
        var ice;
        this.generateIceTimer -= 0.5;
        ice = new Ice(Math.floor(Math.random()*3));
        this.addChild(ice);
    }
}

Save your changes and run the game again. Now the ice boulders should appear at random positions every 0.5 seconds!

Collision Detection

Having run the game, you’ve probably noticed that…

Yep, the ice boulders go right through the penguin. You should do something about this, or your penguin will get lazy and before you know it, get eaten by a killer whale. Make him fight for his life! :]

Start by adding a new variable to the SceneGame constructor, so that the first line looks like this:

var game, label, bg, penguin, iceGroup;

Then add this code in the SceneGame constructor, right after creating the penguin:

// Ice group
iceGroup = new Group();
this.iceGroup = iceGroup;

Here you have created a Group node called iceGroup. Still remember what a Group node is?

A Group is a node that can contain other nodes, just like what the scene’s doing with your sprite and label. You’ll use this group to store all the ice boulders, so that you can manage them all from one place.

Next, add the new group to the scene. Go to where you add nodes to the scene in the SceneGame constructor and add the following code, immediately after where you add the bg node:

this.addChild(iceGroup);

By adding the iceGroup after the background but before the penguin, you make sure that the penguin will always be above the ice. Using Group nodes, you can create a layer system that gives you more control over the rendering order.

Go to SceneGame’s update method. Remember where you generate an ice boulder? Make the following change:

//this.addChild(ice);
this.iceGroup.addChild(ice);

You’re not adding new boulders directly to the scene anymore. Instead, newly created boulders are added to the ice group. But they’ll still be rendered on the scene, because you already added the group to the scene in the constructor.

That’s all fine and good, I hear you say. But what does all this have to do with boulders hitting the penguin? You’re getting there. :] In fact, it’s time to work on the collision detection so that you can find out when a boulder hits the penguin!

One good thing about a Group node is that you have assembled a collection of nodes you are interested in. You’ll go through each boulder in the group and see if it collides with your penguin.

Still inside SceneGame’s update method, add the following lines after the if block:

// Check collision
for (var i = this.iceGroup.childNodes.length - 1; i >= 0; i--) {
    var ice;
    ice = this.iceGroup.childNodes[i];
    if (ice.intersect(this.penguin)){
        this.iceGroup.removeChild(ice);        
        break;
    }
}

A Group node has a childNodes array that keeps track of all of its children. This block of code iterates through each child and checks if it collides with the penguin.

An instance of the Sprite class has an intersect method that you can use to check if two sprites are intersecting. Since Ice and Penguin are both subclasses of Sprite, you can check if the two collide by using this method. If a boulder collides with the penguin, you remove that boulder from the group.

Save main.js and refresh your browser. Now, when a boulder collides with your penguin, it should disappear!

For now, you’ll leave the penguin to imagine that he’s smashing apart those boulders with his beak each time he collides with one. You’ll implement the grim reality soon enough. ;]