impact-banner

Your First HTML5 game using ImpactJS – part 7

Adding Enemies
Start by making new file, name it enemy.js inside the entities folder, and put the following code in it, notice the code is very similar to the item.js file:

	 	 
ig.module(	 	 
'game.entities.enemy'	 	 
).requires(	 	 
'impact.entity'	 	 
).defines(function() {	 	 
EntityEnemy = ig.Entity.extend({	 	 
size: {x:16, y: 16},	 	 
flip: true,	 	 
gravityFactor: 1,	 	 
animSheet: new ig.AnimationSheet('media/enemy.png', 16, 16),	 	 
// allow collision detection with other entities	 	 
type: ig.Entity.TYPE.B,	 	 
checkAgainst: ig.Entity.TYPE.A,	 	 
init: function(x, y){	 	 
this.parent(x, y);	 	 
this.addAnim('walk', 0.1, [0,1,2,1]);	 	 
},	 	 
});	 	 
});	 	 

The main difference between the item.js file and this one is the file for the Animation Sheet.

 

The Enemy spritesheet
The Enemy spritesheet

Add our newly created slime to the game using weltmeister and try it. The Slime is not moving at all, lets fix that.
Add an update method to the enemy file after the init method like this:

update: function(){	 	 
this.parent();	 	 
this.vel.x = 40;	 	 
},	 	 
Add an Enemy to the game
Add an Enemy to the game

If you put the slime near and edge, you’ll see our enemy drop. We want our enemies to be inteligent enough to turn around before falling or when they reach a wall. Start by adding a new function after the update in the enemy.js file:

turnAround: function(){	 	 
//is the next collision tile empty	 	 
var x = this.pos.x + (this.flip ? + 5 : this.size.x - 5);	 	 
var y = this.pos.y + this.size.y + 5;	 	 
if(!ig.game.collisionMap.getTile(x, y)){	 	 
this.flip = !this.flip;	 	 
}	 	 
return this.flip ? -1 : 1;	 	 
}	 	 

Then modify the velocity line in the update function from this:

update: function(){	 	 
this.parent();	 	 
this.vel.x = 40;	 	 
},	 	 

to this:

	 	 
update: function(){	 	 
this.parent();	 	 
this.vel.x = 40 * this.turnAround() ;	 	 
},	 	 

What we are doing here is check on our collision map array if the tile is empty by giving the coordinates to the collisionMap.getTile function. If it is empty then we flip our entity and return a value to be used by the velocity in the update function.
Learn more about the collisionMap.getTile here: http://impactjs.com/documentation/class-reference/map
It should avoid pits now but our enemy will get stuck when reaching a wall. Add the following code after the turnAround function:

turnAround: function(){	 	 
// code hidden	 	 
},	 	 
handleMovementTrace: function(res){	 	 
this.parent(res);	 	 
// flip if collides with a wall	 	 
if(res.collision.x){	 	 
this.flip = !this.flip;	 	 
}	 	 
}	 	 

This internal method of the impact entities does a movement trace against the collision map of our level. So now we can tell if the enemy hit a wall on the x axis forcing the enemy make a flip switch.
You can learn more on handleMovementTrace here: http://impactjs.com/documentation/class-reference/entity#handlemovementtrace
Just one more fix. Since the sprite is not fliping when the enemy flips.
Add a flip for the animation in the update method.

update: function(){	 	 
this.parent();	 	 
this.vel.x = 40 * this.turnAround() ;	 	 
this.currentAnim.flip.x = this.flip;	 	 
},	 	 

Now let’s make the enemy aggressive. Let’s make it hurt our player when it touches it. By default all entities in impact have 10 points of health, if you don’t believe me. add the following code in the draw method of the main.js file and see for yourself:

draw: function() {	 	 
// Draw all entities and backgroundMaps	 	 
this.parent();	 	 
this.font.draw( 'Score: ' + this.score_points, 10, 10, ig.Font.ALIGN.LEFT );	 	 
// display health of the player	 	 
this.font.draw( 'Health: ' + this.player.health, 60, 10, ig.Font.ALIGN.LEFT );	 	 
}	 	 
The Health Indicator
The Health Indicator

Now we want to hurt the player whenever it touches an enemy. Add a check function to the enemy.js file to handle this like this:

handleMovementTrace: function(res){	 	 
// code hidden	 	 
},	 	 
check: function(other){	 	 
other.receiveDamage(4, this);	 	 
this.kill();	 	 
}	 	 

Here we kill the enemy so it won’t suck all of the player enemy at once. But there’s a problem when the player is killed by these critters. Your health is not reset so you respawn with a -2 of health when dying (since we never really destroy our player entity remember)

The health is dropping below 0
The health is dropping below 0

So fix this by updating the kill function of our player.js file:

	 	 
kill: function(){	 	 
// reset the player	 	 
this.pos.x = 64;	 	 
this.pos.y = 144;	 	 
// reset player health	 	 
this.health = 10;	 	 
}	 	 

Lastly we need to let our player defend by itself, so let’s add a mechanic in the style of “Super Mario”.
Update the check function of the enemy.js file like this:

check: function(other){	 	 
// if player is falling and is above the enemy kill the enemy	 	 
if(other.pos.y < this.pos.y && other.vel.y > 0){	 	 
this.kill();	 	 
other.vel.y = - 300;	 	 
}else{	 	 
// .. otherwise damage our player	 	 
other.receiveDamage(4, this);	 	 
this.kill();	 	 
}	 	 
}	 	 

Now we have a real hero. But lets make some improvements to the death of the enemies.
Add a new property called deathFlag to the enemy:

//..	 	 
animSheet: new ig.AnimationSheet('media/enemy.png', 16, 16),	 	 
deathFlag: false,	 	 

then override the kill function of the enemy by adding this method to the enemy.js file:

kill: function(){	 	 
this.deathFlag = true;	 	 
this.checkAgainst = ig.Entity.TYPE.NONE;	 	 
this.vel.x = 0;	 	 
this.currentAnim.flip.y = true;	 	 
}	 	 

This disable the onomy so it wont hurt anymor instead of destroying it. Now update the handleMovementTrace method to match this:

handleMovementTrace: function(res){	 	 
if(this.deathFlag){	 	 
// Pass through the collision tiles	 	 
this.pos.x += this.vel.x * ig.system.tick;	 	 
this.pos.y += this.vel.y * ig.system.tick;	 	 
}else{	 	 
this.parent(res);	 	 
// flip if collides with a wall	 	 
if(res.collision.x){	 	 
this.flip = !this.flip;	 	 
}	 	 
}	 	 
},	 	 

All right, now it feels better when we see the enemies fall down when killed instead of just dissapearing into nothingness.
Also I forgot to put a sound when the players jumps and a background music.
Put this code in the lines where the player jumps on the player.js file:

	 	 
if(ig.input.pressed("jump") && this.standing){	 	 
this.vel.y = this.jump_speed;	 	 
var sound = new ig.Sound( 'media/jump.ogg' );	 	 
sound.play();	 	 
}	 	 

And put the BGM to our game by adding this lines in the init function of the main.js file:

init: function() {	 	 
// older code	 	 
// play the bg music	 	 
ig.music.add( 'media/eric_skiff_we_are_the_resistors.ogg' );	 	 
ig.music.play();	 	 
},	 	 

By the way the music is from Eric Skiff Check out his site for more cool soundtracks.
Well that’s all for this series. By now you have a working game that you can develop and improve with the knowledge you learned so far. Let me know in the comments if you would like to see more tutorials on impact or just let me know your thoughts.
Happy gamedev!
Play the Final Game Here!
Download the final files of this tutorial!

 

Final Code

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
ig.module(
'game.main'
)
.requires(
//'impact.debug.debug',
'impact.game',
'impact.font',
// levels
'game.levels.level_1'
)
.defines(function(){
MyGame = ig.Game.extend({

// Load a font
font: new ig.Font( 'media/04b03.font.png' ),
gravity: 300,
player: null,
score_points: 0,

init: function() {
// Initialize your game here; bind keys etc.
this.loadLevel(LevelLevel_1);
// key binding
ig.input.bind(ig.KEY.LEFT_ARROW,'left');
ig.input.bind(ig.KEY.RIGHT_ARROW,'right');
ig.input.bind(ig.KEY.SPACE,'jump');

this.player = this.getEntitiesByType( EntityPlayer )[0];

// play the bg music
ig.music.add( 'media/eric_skiff_we_are_the_resistors.ogg' );
ig.music.play();
},

update: function() {

// Update all entities and backgroundMaps
this.parent();

// Add your own, additional update code here

if( this.player ) {
this.screen.x = this.player.pos.x - ig.system.width/2;
this.screen.y = this.player.pos.y - ig.system.height/2;
}

},

draw: function() {
// Draw all entities and backgroundMaps
this.parent();
this.font.draw( 'Score: ' + this.score_points, 10, 10, ig.Font.ALIGN.LEFT );
// display health of the player
this.font.draw( 'Health: ' + this.player.health, 60, 10, ig.Font.ALIGN.LEFT );
}

});
// Start the Game with 60fps, a resolution of 320x240, scaled
// up by a factor of 2
ig.main( '#canvas', MyGame, 60, 320, 240, 2 );
});

player.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
ig.module(
'game.entities.player'
)
.requires(
'impact.entity'
)
.defines(function() {
EntityPlayer = ig.Entity.extend({
size: {x:16, y: 16},
flip: false,
gravityFactor: 1,
maxVel: {x: 100,y: 150},
friction: {x: 300,y: 0},
speed: 150,
jump_speed: -200,
animSheet: new ig.AnimationSheet('media/player.png', 16, 16),

type: ig.Entity.TYPE.A,
init: function(x,y,settings) {
this.parent(x, y, settings);
this.addAnim('idle',1,[4]);
this.addAnim('walk', 0.1, [0, 1, 2, 1]);
this.addAnim('jump', 1, [3]);
},
update: function(){
this.parent();

if(ig.input.state("left")){
this.accel.x = -this.speed;
this.flip = true;
} else if(ig.input.state("right")){
this.accel.x = this.speed;
this.flip = false;
}else{
this.accel.x = 0;
}
if(ig.input.pressed("jump") &amp;&amp; this.standing){
this.vel.y = this.jump_speed;
var sound = new ig.Sound( 'media/jump.ogg' );
sound.play();
}

// animations
if(!this.standing){
this.currentAnim = this.anims.jump;
}else if(this.vel.x == 0){
this.currentAnim = this.anims.idle;
}else{
this.currentAnim = this.anims.walk;
}

this.currentAnim.flip.x = this.flip;

// check death from falling
if(this.pos.y &gt; ig.system.height){
this.kill();

}
},

kill: function(){
// reset the player
this.pos.x = 64;
this.pos.y = 144;
// reset player health
this.health = 10;
}
});
});

item.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
ig.module(
'game.entities.item'
)
.requires(
'impact.entity'
)
.defines(function() {
EntityItem = ig.Entity.extend({

size: {x:16, y: 16},
flip: false,
gravityFactor: 0,
animSheet: new ig.AnimationSheet('media/item.png', 16, 16),

// allow collision detection with other entities
type: ig.Entity.TYPE.B,
checkAgainst: ig.Entity.TYPE.A,

init: function(x, y){
this.parent(x, y);
this.addAnim('spin', 0.1, [0,1,2,3]);
},

check: function(other){
this.kill();
ig.game.spawnEntity(EntityFedback, this.pos.x, this.pos.y);
// play a sound
var sound = new ig.Sound( 'media/pickup.ogg' );
sound.play();
// add to the score
ig.game.score_points += 10;
}

});

EntityFedback = ig.Entity.extend({
size: {x:16, y: 16},
flip: false,
gravityFactor: 0,
animSheet: new ig.AnimationSheet('media/feedback.png', 16, 16),
init: function(x, y){
this.parent(x, y);
this.addAnim('feedback', 0.1, [0,1,2,3]);
this.killTimer = new ig.Timer();
},
update: function(){
this.parent();
// destroy after a second
if(this.killTimer.delta() &gt; 0.5){
this.kill();
}
},
});
});

enemy.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
ig.module(
'game.entities.enemy'
).requires(
'impact.entity',
'game.entities.player'
).defines(function() {
EntityEnemy = ig.Entity.extend({

size: {x:16, y: 16},
flip: false,
gravityFactor: 1,
animSheet: new ig.AnimationSheet('media/enemy.png', 16, 16),
deathFlag: false,

// allow collision detection with other entities
type: ig.Entity.TYPE.B,
checkAgainst: ig.Entity.TYPE.A,
init: function(x, y){
this.parent(x, y);
this.addAnim('walk', 0.1, [0,1,2,1]);
},

update: function(){
this.parent();
if(this.deathFlag){
return;
}

this.vel.x = 40 * this.turnAround() ;
this.currentAnim.flip.x = this.flip;
},

turnAround: function(){
//is the next collision tile empty
var x = this.pos.x + (this.flip ? + 5 : this.size.x - 5);
var y = this.pos.y + this.size.y + 5;
if(!ig.game.collisionMap.getTile(x, y)){
this.flip = !this.flip;
}

return this.flip ? -1 : 1;
},

handleMovementTrace: function(res){
if(this.deathFlag){
// Pass through the collision tiles
this.pos.x += this.vel.x * ig.system.tick;
this.pos.y += this.vel.y * ig.system.tick;
}else{
this.parent(res);
// flip if collides with a wall
if(res.collision.x){
this.flip = !this.flip;
}
}
},

check: function(other){
// if player is falling and is above the enemy kill the enemy
if(other.pos.y &lt; this.pos.y &amp;&amp; other.vel.y &gt; 0){
this.kill();
other.vel.y = - 300;
}else{
// .. otherwise damage our player
other.receiveDamage(4, this);
this.kill();
}
},

kill: function(){
this.deathFlag = true;
this.checkAgainst = ig.Entity.TYPE.NONE;
this.vel.x = 0;
this.currentAnim.flip.y = true;
}

});
});

Hey, like this post? Why not share it with a buddy?

Show all Impact Tutorials