Enchant.js Crash Course

by Patrick Casao, Cal Poly

With this tutorial, we will explore some of the features of enchant.js and use them to create a simple game. We will cover backgrounds, sprites, user input, animation, labels, collisions, game logic, and sound.


Table of Contents
00 Resources and Setup
01 Adding Your First Sprite
02 Adding the Player Sprite
03 Player Input (Keyboard)
04 Player Input (Mouse)
05 Animation
06 Labels
07 Collisions
08 Game Logic
09 Sound
10 References


00 Resources and Setup

First, click on this link to the project files here.

If you don't already have one, you'll need to create an account in order to fork this code. Once you do, you can work on this project right out of your browser, and it already has all the assets that we need. The interface is pictured below in figure 01.



Figure 01: Codeleap Interface

The top left frame is where the game will apear. The dropdown menu in the center allows us to select which text file we're editing. For this tutorial we will only make changes to the main.js file. On the right side is the list of the assets that we will use. To run your game, simply click on the blue RUN button up and to the right of the screen.

Let's look at the main.js file. Some of the comments are numbered. The number before the comment indicates the section of the tutorial in which we will make changes.

	
enchant(); //the magic words that start enchant.js
//Stage Variables
var moveSpeed = 4;
var health = 5;
var stgWidth = 320;
var stgHeight = 320;


//02 Player Class
Player = Class.create(Sprite, {
    initialize: function() {

        //03 Bind Keys
        
        //04 Mouse Variables
    },

    onenterframe: function() {
        
        //03 Player Controls
        
        //04 Mouse Update
    }
});

//05 Gem Class
Gem = Class.create(Sprite, {
    initialize: function() {
        Sprite.call(this, 16, 16);
        this.image = game.assets['diamond-sheet.png'];
    },

    onenterframe: function() {

        //Rotating using scaleX
        
        //07 Collision Check
    }
});

//08 Bomb Class
Bomb = Class.create(Sprite, {
    initialize: function() {
        Sprite.call(this, 16, 16);
        this.image = game.assets['icon0.png'];
        this.x = Math.random() * (stgWidth - 16);
        this.y = Math.random() * (stgHeight - 16); //Account for the bottom part
        if (this.y < 50) {
            this.y = 50;
        }

        this.frame = 24;
    },

    onenterframe: function() {
        if (this.age === 60) {
            game.rootScene.removeChild(this);
        }

        if (this.intersect(player)) {
            player.health--;
            game.rootScene.removeChild(this);
            console.log("ouch!");
        }

        if (this.age % 10 === 0) {
            if (this.frame === 25) {
                this.frame = 24;
            } else {
                this.frame++;
            }
        }
    }

});


//Begin game code
window.onload = function() {
    game = new Game(stgWidth, stgHeight);
    //Preload images
    //Any resources not preloaded will not appear
    game.preload('icon0.png', 'diamond-sheet.png');

    game.onload = function() { //Prepares the game
        //01 Add Background
        
        //02 Add Player
        
        //05 Add Gem
        
        //06 Create Label
        
        //08 Health Label
        
        //04 Touch Listener
        
        //Game Condition Check
        game.rootScene.addEventListener('enterframe', function() {
            //08 Game Over
            
            //08 Make Bomb Generator
        });

    }
    game.start(); //Begin the game
}
	

This is the skeleton code that we will be adding code to throughout the tutorial.

Note: For debugging purposes, you'll want to examine the JavaScript Console. You can view errors and output messages to it (similar to printfs) using:

	
console.log("input log text here");
	

Now that we're ready, let's start by adding a background.


01 Adding Your First Sprite

Let's begin by adding a stationary sprite. First we have to preload the file. Add the item: 'bg.png' to the preloader so that it looks like this:

	
game.preload('icon0.png', 'diamond-sheet.png', 'bg.png');
	

Note: Preloaded assets will be indexed from the current directory of the .html file. This is enough for our current tutorial. However, if you start working on this locally, I highly recommend organizing your assets by type. For example, you would store bg.png in an images folder and preload it by adding 'images/bg.png' to the preloader.

Now that it's preloaded, we can add the sprite.

Let's create a new sprite called bg, with the length and width of the stage. Add this code right after the 01 Add Background comment:

	
bg = new Sprite(stgWidth, stgHeight);
	

Set the sprite's image to the asset that we've preloaded:

	
bg.image = game.assets['bg.png'];
	

Now that it's loaded, we can add it to the stage like so:

	
game.rootScene.addChild(bg);
	

This will add the background sprite at the location (0, 0). Confirm the background sprite has been added by clicking on the RUN button in your browser. You should have a blue background with a black bar along the top. This is shown below in figure 02.



Figure 02: Adding the Background Sprite

Let's move on to adding player sprites.


02 Adding a Player Sprite

Unlike the background sprite, we will create a class for the player sprite. An empty class is provided under the comment 02 Player Class. Let's begin by examining the empty class. It contains two functions: initialize and onenterframe.

Under initialize, we can initialize attributes like x-position, y-position, scale, frame, etc. You can find links to more about the attributes in the References section.

Under onenterframe, we can add game loop code. Code added here will be run every frame. We can add code that responds to user input here, as well as different condition checks.

Let's start by initializing attributes. Under the initialize function, set the sprite size like so:

	
Sprite.call(this, 16, 16);
	
Set the sprite's image to the spritesheet in the images folder:
	
this.image = game.assets['icon0.png'];
	
Let's place it in the center of the stage by setting its x to the center of the stage width, and the center of the stage height.
	
this.x = stgWidth/2;
this.y = stgHeight/2;
	
Even though we've set the image of the sprite, we still have to set which frame of the sprite sheet we want. For now, we're going to use the monkey sprite from the icon0.png spritesheet. enchant.js counts sprites in the sprite sheet from left to right, row by row moving at the sprite size you specified in Sprite.call Set the current frame to the monkey sprite like so:
	
this.frame = 44;
	
We will also set the player's health to 4 like so:
	
this.health = 4;
	
This will be addressed in a later section. Before moving forward, let's confirm that you were able to add the sprite successfully. Even though the player sprite has been created and initialized, it still hasn't been added to the scene. Under the comment 02 Add Player add the player sprite using this code:
	
player = new Player();
game.rootScene.addChild(player);
	
Press the RUN button to confirm that the player has been added. This is shown below in figure 03.



Figure 03: Adding the Player Sprite

Now that we've added the player to the game, let's have it respond to keyboard input. Go back to the Player class.


03 Player Input (Keyboard)

By default, enchant.js has six input buttons that may be bound to any key with an ASCII value. These buttons are left, right, up, down, a, and b. We will be binding them soon, but for now let's leave them at their default bindings.

Move down to the onenterframe function. As mentioned before, code within this function will be run every new frame. In order to make the sprite move, we will add a couple of if/else statements that respond to player input. In the onenterframe function under 03 Player Controls add the following code:

	
if(game.input.left && !game.input.right){
    this.x -= moveSpeed;
}
else if(game.input.right && !game.input.left){
    this.x += moveSpeed;
}
	
This will move the Player sprite to the left by the speed defined by the variable moveSpeed if the left button is held down, and to the right right button is held down. Add the following code below it in order to move up and down:
	
if(game.input.up && !game.input.down){
    this.y -= moveSpeed;
}
else if(game.input.down && !game.input.up){
    this.y += moveSpeed;
}
	
Note: Remember that increasing the y-value will actually move the sprite downwards.

Now we should be able to move the sprite using the arrow keys. Test this by clicking the RUN button on codeleap.

Let's go back and bind the buttons to different keys. To do this, we use game.keybind(ASCII_VALUE, 'BUTTON_NAME') where ASCII_VALUE is the integer value that represents a key and BUTTON_NAME is the name of one of the buttons.

Under 03 Bind Keys add:
	
game.keybind(65, 'left');
game.keybind(68, 'right');
game.keybind(87, 'up');
game.keybind(83, 'down');
	
This will bind the direction buttons to the W, A, S, and D keys. Confirm this change by clicking on the RUN button and testing it.

Note: To test the key bindings, click on the "New window" button underneath the game frame. A new window should pop up and it should read in your WASD key input just fine. Now that we have keyboard input working, let's add mouse input.


04 Player Input (Mouse)

enchant.js treats mouse clicks like touch input and vice versa. We will add mouse input as an alternative method of moving the player. After clicking on a particular spot on the screen, the player sprite will move towards that spot.

First, let's add some variables to the player sprite class, as well as some update code.

Under 04 Mouse Variables add:

	
this.tx = this.x;
this.ty = this.y;
	
This initiailizes the variables tx and ty to x and y, respectively. tx and ty will represent the coordinates of the mouse click location.

Under 04 Mouse Update add:
	
this.x += (this.tx - this.x)/4;
this.y += (this.ty - this.y)/4;
	
This will add 1/4th of the distance from tx to x and ty to y until the player sprite reaches the mouse click location.

Under 04 Touch Listener add:
	
game.rootScene.addEventListener('touchend', function(e){
    player.tx = e.x-16;
    player.ty = e.y-16;
});
	
This will add an event listener that responds when a touch or mouse button is released. This means that until you actually pull your finger away or let go of the mouse button, this event will not occur. Every time this event occurs, the tx and ty are set to the click location. We subtract 16 from its x and y in order to account for the size of the sprite.

Confirm that the mouse input works by clicking the RUN button. The player sprite should now follow your mouse clicks.

Nowever, now we've broken keyboard-based input. If you try to move, you can't move very far from the location defined by tx and ty. To remedy this, we're going to force tx and ty to update every time we press one of the input keys.

Under 03 Player Controls, add this.tx= to the beginning of the left and right key input code, and this.ty= to the beginning of the up and down key input code. It should look something like this:
	
this.tx = this.x -= moveSpeed;
	
Click on the RUN button in your browser to confirm that keyboard controls have been fixed.

Now that we have mouse and keyboard input working, let's add some animation.


05 Animation

For this step we will be using an asset we preloaded in step 01 called diamond-sheet.png. Let's begin by creating another class that uses the diamond sheet. We'll simply gall it Gem.

The bare bones for this class is provided under 05 Gem Class. Add this code to Gem's initialize function:

	
this.x = 3 * stgWidth/4;
this.y = stgHeight/4;
this.frame = 0;
	
These x and y coordinates place the gem in the top right corner of the screen. We'll set the frame to the first frame of the animation, which is indexed at 0.

Add this code to the Gem's onenterframe function:
	
if(this.age % 2 === 0){
    if(this.frame == 5){
        this.frame = 0;
    }
    else{
        this.frame++;
    }
}
	
In this code we're using an attribute of the sprite called age. It's incremented once per frame. Using the mod function on age, we update the sprite every 2 frames. Once it reaches the last frame of the animation, we reset it to the first frame.

Finally, let's add the Gem sprite to the game. Under 05 Add Gem add:
	
gem = new Gem();
game.rootScene.addChild(gem);
	
Confirm that the animation works by clicking on the RUN button. It should appear like figure 04 below.



Figure 04: Game with Gem Added

Another neat little animation trick we can do is make it appear as if the sprite is rotating. If we add this code under the frame updating code in onenterframe:

	
this.scaleX = Math.sin(this.age * .1);
	
The sprite will scale in the x-direction between 1 and -1, giving it the appearance of a flat shape rotating in place.

Note: This effect is more succesful with sprites that are vertically symmetrical.

Now that we have a handle on animation, let's move onto labels.


06 Labels

We can add text to the screen using labels. Labels are a simple way to keep track of the game score, player health, and other metrics. In this section, we will add a label that will keep track of the game's score. Under 06 Create Label add:

	
game.score = 0;
scoreLabel = new Label("Score: ");
	
This will initiate the game's score to 0 and create a new label called scoreLabel made up of the text "Score: ". Let's add an eventlistener to score. Add this:
	
scoreLabel.addEventListener('enterframe', function(){
    this.text = "Score:"+game.score;
});
	
This will update the text of scoreLabel to the value in game.score. We will use this soon. Also note that we can change the attributes of the label. After the eventListener code, add:
	
scoreLabel.x = stgWidth/2;
scoreLabel.color = "white";
	
This code moves the scoreLabel to the center of the screen and sets its color to white. You can move the score to the left side of the screen by removing the line that changes its x attribute.

Finally, let's add this:
	
game.rootScene.addChild(scoreLabel);
	
This will add the label to the scene. Confirm that the label is correct by clicking on the RUN button. The scene with the scoreLabel added is visible in figure 05 below.



Figure 05: Game with Score Label Added

Now that we've got a score counter, let's move on to collision detection.


07 Collision Detection

To put it simply, collision detection involves checking whether or not two things are touching. What happens after that point is up to you. In this section, we will simply remove the gem once it collides with the player sprite.

In the Gem class code, under 07 Collision Check add:

	
if(this.intersect(player)){
    game.score += 100;
    game.rootScene.removeChild(this);
}
	
This will check if the gem is intersecting with the player sprite, and makes a call to game.rootScene to remove it from the game. I've also included a lin to increase the game's score by one. We will address this, as well as explore collision detection further in the next section: Game Logic.


08 Game Logic

Now that we have the necessary tools under our belt, let's turn this code into a simple game. We will have the game spawn gems for the player to collect, and bombs for the player to avoid. Using the collision detection code, every gem collected will give the player 100 points, and every bomb collected will subtract one point of health.

Let's begin by changing the x and y attributes of the Gem class. Change it to this:

	
this.x = Math.random() * (stgWidth - 16);
this.y = Math.random() * (stgHeight - 16);

if(this.y < 50){
    this.y = 50;
}
	
This will make it so that gems can appear anywhere on the screen. The condition below this change prevents gems from appearing above the black bar. Next, let's change the gem's collision detection code. Under 07 Collision Check right before score += 100;, add:
	
gem = new Gem();
game.rootScene.addChild(gem);
	
This will add a new gem to the screen right before the current gem is removed. Since the bomb class has already been written, we just need to include code that generates bombs. In the game's enterframe function, under 08 Make Bomb Generator add:
	
if(player.age % 30 === 0){
    bomb = new Bomb();
    game.rootScene.addChild(bomb);
}
	
This will add a bomb to the scene every thirty frames. Now that bombs are being generated, let's add a label that helps us keep track of health. Under 08 Health Label add:
	
healthLabel = new Label("Health: ");
healthLabel.addEventListener('enterframe', function(){
    this.text = "Health: "+player.health;
	
    if(player.health <= 2){
        this.color = "red";
    }
});

healthLabel.color = "white";
healthLabel.x = 6 * stgWidth/8;
game.rootScene.addChild(healthLabel);
	
This will create a new label that keeps track of a player's health. When the player's health drops below 2, it will change the color of the label from white to red. Now that we can track player health, let's add the game over condition.

Under 08 Game Over add:
	
if(player.health <= 0){
    game.end();
}
	
This will end the game as soon as the player's health is less than or equal to 0. Click on RUN in your browser to test your simple game. The completed game should look something like figure 06 below.



Figure 06: Completed Simple Game

We won't be able to implement sound on 9leap, but let's dicuss it in the next section.


09 Sound

For now we can't use sound on code.9leap.net, as it doesn't allow us to add sound files to the resources. I'll still explain how to use it, in case you'd like to download the resource files and work on it on your own.

First, we preload the sound file like we did with the image files. For our example, let's say there's a sound file called 'ping.wav'. Just add it to the end of the preload function like so:

	
game.preload('icon0.png', 'diamond-sheet.png', 'bg.png', 'ping.wav');
	
Once it's been preloaded, we can use it like any of our other assets. Add this under Gem's onenterframe function with the collision detection code:
	
game.assets['ping.wav'].play();
	
Now when the player touches the gem, the sound will play as the gem is removed from the game.

If you want to do something like play music that loops, begin by preloading the song file, playing it in the game's initialize function, and add this code to the game's enterframe function:
	
if(this.bgm.currentTime >= this.bgm.duration ){
    this.bgm.play();
}
	
This will start the music up again once the track stops playing.


10 References

I've included some links that might be useful if you want to look into using enchant.js more.

enchant.js Official Site
This is the official enchant.js website. You can download the source file to work on it for yourself here.

enchant.js Documentation
This is the Documentation section of the official enchant.js website. You can find out more about Sprite attributes and other classes here.

code.9leap.net
This is the code site for 9leap. You can write example code, share it with others, and have them fork it here.

9leap Games
This is a site that hosts games built in enchant.js. It is in Japanese, so it's a little tricky to navigate. I recommend throwing the URL into google translate.