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
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.
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.
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.
Let's move on to adding player sprites.
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);
this.image = game.assets['icon0.png'];
this.x = stgWidth/2;
this.y = stgHeight/2;
Sprite.call
Set the current frame to the monkey sprite like so:
this.frame = 44;
this.health = 4;
player = new Player();
game.rootScene.addChild(player);
Now that we've added the player to the game, let's have it respond to keyboard input. Go back to the Player class.
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;
}
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;
}
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.
game.keybind(65, 'left');
game.keybind(68, 'right');
game.keybind(87, 'up');
game.keybind(83, 'down');
W
, A
, S
, and D
keys. Confirm this change by clicking on the RUN button and testing it.WASD
key input just fine.
Now that we have keyboard input working, let's add mouse input.
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;
tx
and ty
to x
and y
, respectively. tx
and ty
will represent the coordinates of the mouse click location.
this.x += (this.tx - this.x)/4;
this.y += (this.ty - this.y)/4;
tx
to x
and ty
to y
until the player sprite reaches the mouse click location.
game.rootScene.addEventListener('touchend', function(e){
player.tx = e.x-16;
player.ty = e.y-16;
});
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.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.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;
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;
onenterframe
function:
if(this.age % 2 === 0){
if(this.frame == 5){
this.frame = 0;
}
else{
this.frame++;
}
}
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.
gem = new Gem();
game.rootScene.addChild(gem);
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);
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: ");
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;
});
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";
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.
game.rootScene.addChild(scoreLabel);
scoreLabel
added is visible in figure 05 below.
Now that we've got a score counter, let's move on to 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);
}
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.
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;
}
score += 100;
, add:
gem = new Gem();
game.rootScene.addChild(gem);
enterframe
function, under 08 Make Bomb Generator add:
if(player.age % 30 === 0){
bomb = new Bomb();
game.rootScene.addChild(bomb);
}
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);
game over
condition.
if(player.health <= 0){
game.end();
}
We won't be able to implement sound on 9leap, but let's dicuss it in the next section.
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');
Gem
's onenterframe
function with the collision detection code:
game.assets['ping.wav'].play();
initialize
function, and add this code to the game's enterframe
function:
if(this.bgm.currentTime >= this.bgm.duration ){
this.bgm.play();
}
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.