Chapter 4. Building A Game

In the following chapter, we are going to build a game called Resident Raver, in which the player controls an unnamed hero who is trying to escape a college dorm overrun by raver zombies. The hero has several weapons at his disposal to help him escape, while also navigating platform-based obstacles. I have gone ahead and created all the assets you will need for the final game.

Before moving onto the next section, make sure you copy over the contents of the media folder that is included in the book’s example files folder. This will contain all the assets you will need for the rest of the book. In it you will find sprites, map tiles, and sounds.

The media folder that contains the assets you will need to continue building your game.
Figure 4-1. The media folder that contains the assets you will need to continue building your game.

It’s okay to replace the default media directory with the one we will be using for this book.

Creating Our First Level

Now that we have learned about creating graphics for our game, we can start building our first level. Let’s open up Weltmeister and create a simple level. When you open up Weltmeister for the first time, you will see that an untitled.js file has been created for you, but that the level is empty. Before we can even start creating our level, we will need to add our map tile sprite sheet. Create a new layer and call it main. This is where we will start drawing our level. You can create this new layer by clicking the plus icon on the top right of the screen next to the Layers label.

This is what you will see after creating your new layer.
Figure 4-2. This is what you will see after creating your new layer.

When a new layer is created, its bounding box will be displayed showing the borders of the layer. You can’t draw tiles outside of this box. This is based on the layer’s dimensions, which are configured as part of the layer’s properties. Here is a summary of what each property represents:

Name

This is the name of the layer. It can be anything you want.

Tileset

This is the path to the Tileset image file you want to use for this layer. Clicking on this property will bring up a list of directories in your project file.

Tilesize

This represents the size of each tile in your sprite sheet. By default, it is set to 8.

Dimensions

This represents how large the layer is. By default, it is set to 30×30 tiles. Since tiles are set to 8 pixels by default, this layer is 240×160 pixels.

Distance

This is the distance from the camera, similar to Z index in HTML. This is useful for background layers you want to scroll at a different speed than the foreground layers to create a parallax scrolling effect.

Is Collision Layer

This toggle allows you to use the layer for collision detection, which we will cover later on.

Pre-Render In Game

This will pre-render your map, which can help increase the performance of your game, especially on mobile devices, but you will not be able to have animated tiles.

Repeat

This is also a background layer property that allows you to repeat your layer when it scrolls by in the background. Think of this more as a texture that is repeated inside of the layer.

Now we are ready to make our first level. Let’s click on the Tileset field and select our map tiles. After clicking on the Tileset input field, you should see the root directory of your project from a drop-down menu, as seen in Figure 4-3. Select media, and there you can select dorm-tiles.png. You will also need to set the Tilesize to 16. Make sure to apply the changes so your selections are saved.

Use the drop-down menu to select the media directory where our tile sprites are.
Figure 4-3. Use the drop-down menu to select the media directory where our tile sprites are.

Now, click inside the map editor and press the space bar to bring up the tile painter (Figure 4-4).

All the tiles you can paint your level with.
Figure 4-4. All the tiles you can paint your level with.

The tile painter is very easy to use. While your tile set is visible, simply select the tile you want to use by clicking it with your mouse. The tile set will disappear and you will now be able to place your selected tile by clicking anywhere in the layer. You can also click and drag to create many tiles of the same artwork. When you want to change tiles, press the space bar again, select a new tile, and resume clicking to place the new tiles.

Notice the yellow box at the beginning of your sprite sheet that looks empty? Selecting this allows you to erase tiles you have already painted. Weltmeister automatically creates the empty tile for you so you don’t need to add it to your sprite sheet. Let’s draw the foundation of our level, shown in Figure 4-5.

Our game’s first level.
Figure 4-5. Our game’s first level.

Note

When you start creating your own level, feel free to experiment with the tiles and add some variety to your textures. You don’t have to follow this example 100%, but I wanted to create something that was quick to make and had two different ground heights to it. It’s also important to note that I never start my maps at exactly 0,0, in case I need more space at the top of the lefthand side later on.

I know this level doesn’t look very exciting yet but, don’t worry, we will get into more complex level creation later on.

Saving/Loading Maps in Weltmeister

Save your map by hitting Save at the top of the editor. You will notice that, by default, Weltmeister wants to put levels in the lib/game/levels/ directory. This is very important, because this is where Impact will automatically look for your game’s levels. Later on, we will be able to simply tell Impact to load your level by name instead of having to pass it the full directory path.

Levels are automatically saved inside the game’s levels directory.
Figure 4-6. Levels are automatically saved inside the game’s levels directory.

Let’s name our level dorm1.js. Note that we have to add the .js file extension to our level. The level file is actually a JSON file. The editor will give you an error if you forget to add the correct extension.

Note

It’s important to note that you will need to have PHP set up in order to save. The editor itself is built with JavaScript, but the save API uses PHP. This should be handled automatically if you’re using the recommended MAMP or XAMP applications discussed in Chapter 1.

You can also easily load any level you have created by selecting Load from the top menu.

It is easy to load any level you have already created.
Figure 4-7. It is easy to load any level you have already created.

You will see a list of all the maps you have created. By default, Weltmeister automatically attempts to load the last level you were working in.

CollisionMaps

Now that we have our level’s tiles in place we need to set up a collision layer. This tells Impact what tiles are passable and impassable to the game’s entities. To set this up, we need to create a new layer called collision and set the Tilesize to 16. Now, you can select Is Collision Layer from the layer options, and a default set of collision tiles will automatically be loaded for you. Once you have the collision layer in place, let’s start painting.

These are the default sprites for the collision tiles.
Figure 4-8. These are the default sprites for the collision tiles.

As seen in Figure 4-8, there are a lot of different collision tiles for us to choose from. Most of these tiles help support collisions with slopes. We are going to focus on the first solid tile, which is the pink square in the upper-left corner. Let’s paint on top of our main level’s walls.

Our level with the collision tiles in place.
Figure 4-9. Our level with the collision tiles in place.

Usually, I put the collision layer underneath the main layer. You can reorder layers at any time by simply dragging them around. Now, when we create our player and monsters, Impact will make sure they don’t fall through the floor.

It looks like we are finally ready to start building our game!

The Main Class

When setting up a new Impact project from the template project, you will see a main.js file in your game directory. The main class is the entry point to your application and will contain some of the core logic, such as binding keyboard events and logic for making the camera follow the player. Most importantly, the main file also defines and loads any required files or global functions in your game. Here is the default main.js class you will start with:

1    ig.module(
2        'game.main'
3    )
4    .requires(
5        'impact.game',
6        'impact.font'
7    )
8    .defines(function(){
9
10        MyGame = ig.Game.extend({
11
12            // Load a font
13            font: new ig.Font( 'media/04b03.font.png' ),
14
15
16            init: function() {
17                // Initialize your game here; bind keys etc.
18            },
19
20            update: function() {
21                // Update all entities and backgroundMaps
22                this.parent();
23
24                // Add your own, additional update code here
25            },
26
27            draw: function() {
28                // Draw all entities and backgroundMaps
29                this.parent();
30
31
32                // Add your own drawing code here
33                var x = ig.system.width/2,
34                y = ig.system.height/2;
35
36                this.font.draw( 'It Works!', x, y, ig.Font.ALIGN.CENTER );
37            }
38        });
39
40
41        });
42
43
44    // Start the Game with 60fps, a resolution of 320×240, scaled
45        // up by a factor of 2
46        ig.main( '#canvas', MyGame, 60, 320, 240, 2 );
47
48    });
49

Let’s take a moment to go through some of the high-level code.

ig.module(
    'game.main'
)

The above code represents the namespace of your game. As you can see, this also defines the name of your main class.

.requires(
    'impact.game',
    'impact.font'
)

The first block of code above defines the class name and, as a result, file name of the module. The second block specifies which classes are needed by the game. Our main class will inherit from the Game class, and the Font class will be used to instantiate a font we’ll use for onscreen text display. These classes will automatically load when your game is run for the first time.

Next, everything in the .defines(function(){ ... }) block of code is your game logic. As you can see, Game is extended as described, and the Font class is used to populate a property called font with the font class and its font sprite sheet:

.defines(function(){

    MyGame = ig.Game.extend({

          // Load a font
          font: new ig.Font( 'media/04b03.font.png' ),

Then, we define some scaffolding code for init(), update(), and draw(). The draw() method is the only one with executable code in it. The default class needs to re-render the font on each draw call, so this default code simply gets the x,y position of where the text field should go, so the engine knows where to draw the font graphic.

Before moving on, there is one more thing that is important to highlight from the last few lines of the main.js file:

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

This is the code that initializes your game. As you can see, we pass the ID of the Canvas element to our game’s constructor, a name for our game instance, and the frame rate and size into the ig.main constructor. The last value of 2 represents the scale of your game. This will upscale all of your game’s graphics by 2.

Customizing the Main Class

To get started, let’s delete the font code from the draw method, but make sure you leave this.parent(). We can also now delete the font variable from the beginning of the class.

Now with the “It Works!” text removed, the first thing we want our game to do when it starts is load the level we just created. Change the .requires(...) block to load the level:

.requires(
    'impact.game',
    'game.levels.dorm1'
)

Unfortunately, if you refresh your game, nothing is going to display. We will need to tell the game to load our level. Add the following code to your init() method:

init: function() {
    this.loadLevel( LevelDorm1 );
},

Now, refresh your game.

The level loaded when the game is refreshed.
Figure 4-10. The level loaded when the game is refreshed.

Key Binding

Impact has an easy-to-use input class, ideal for capturing keyboard input, which can be found in the ig.input namespace. To capture keyboard events, we will need to bind the key press event to the desired key in the input class. Let’s put the following code at the beginning of the init() function in main.js, just above our load level code:

// Bind keys
ig.input.bind( ig.KEY.LEFT_ARROW, 'left' );
ig.input.bind( ig.KEY.RIGHT_ARROW, 'right' );
ig.input.bind( ig.KEY.X, 'jump' );
ig.input.bind( ig.KEY.C, 'shoot' );

For our game, we are going to track the left and right arrows along with the X and C keys. This is the first step in setting up controls for your game. In a little while, we’ll set up code to react to the left, right, jump and shoot commands those keys will trigger.

There is a list of constants that contains all the keys Impact can use in the ig.KEY class. To use them, simply apply a custom label to a key so that when we poll for input during game play, we will be able to react to the desired event when each key is pressed.

Creating the Player Class

The player will be the first entity we build for our game. Entities are anything that exist in the level that are not part of the map. Monsters, bullets, doors, triggers, etc., are all considered entities. Our player class will extend the core entity.js class so it can inherit some basic behavior to get us started. Let’s begin by creating a new player.js file in the lib/game/entities directory.

Our player class goes in the entities directory.
Figure 4-11. Our player class goes in the entities directory.

Now, add the following code to our file:

1    ig.module(
2        'game.entities.player'
3    )
4    .requires(
5        'impact.entity'
6    )
7    .defines(function(){
8        EntityPlayer = ig.Entity.extend({
9
10        });
11    });

This is the basic structure for creating entities in Impact. As discussed previously, we define the module name and reference any required classes, then define the class itself, extending ig.Entity. At this point, however, nothing will happen if you refresh your game. We still need to set up the player and add it to the level. To do that, let’s add some properties to this class.

Using Your Sprite Sheet

Start by setting up an animation sheet. Add the following to the EntityPlayer code block:

EntityPlayer = ig.Entity.extend({
    animSheet: new ig.AnimationSheet( 'media/player.png', 16, 16 ),
});

This tells our player that it will use player.png in the media folder and that its tiles are 16×16. We are also going to need to define some values for the size and offset of the player. We’ll add the following underneath where we set up our animation sheet:

size: {x: 8, y:14},
offset: {x: 4, y: 2},
flip: false,

The size property represents the actual size of the player. The offset property describes any change in the player size needed to make collisions more accurate. In this case, we’re offsetting the bounding box used for collisions by 4 pixels on the left and right, and 2 pixels on top and bottom. By making the collision area smaller than the sprite, we can better account for the transparent space around the graphic. Finally, we don’t flip the player, so it remains oriented in its original direction.

Adding Simple Physics

Next let’s set up some physics properties, such as velocity, friction, rate of acceleration in the ground and air, and jump strength.

maxVel: {x: 100, y: 150},
friction: {x: 600, y: 0},
accelGround: 400,
accelAir: 200,
jump: 200,

These properties define how our player can move in the environment. Impact handles all of the physics calculations for us. Once we get the player up and running, you should feel free to tweak these values to see how they affect your game.

Defining Animation Sequences

With the player’s core values out of the way, we can look into setting up animation sequences. Create an init() method underneath where we defined the properties in the player class and add the following code to it:

init: function( x, y, settings ) {
    this.parent( x, y, settings );
    this.addAnim( 'idle', 1, [0] );
    this.addAnim( 'run', 0.07, [0,1,2,3,4,5] );
    this.addAnim( 'jump', 1, [9] );
    this.addAnim( 'fall', 0.4, [6,7] );
},

This function passes the x,y and settings values up to the parent’s init() method. This is very important, since entities need to know their starting x,y positions and any settings assigned to them when being created in the level. You can also pass in additional values through the level editor, which get attached to the settings object during the construction of the entities.

As discussed earlier, it’s easy to set up animations. Use the entity class’s addAnim() method and pass it an ID (or name) for the animation, along with the duration and an array for the frames from the sprite sheet. Before we move on, let’s make sure your player class looks like this:

1    ig.module(
2        'game.entities.player'
3    )
4    .requires(
5        'impact.entity'
6    )
7    .defines(function(){
8        EntityPlayer = ig.Entity.extend({
9            animSheet: new ig.AnimationSheet( 'media/player.png', 16, 16 ),
10            size: {x: 8, y:14},
11            offset: {x: 4, y: 2},
12            flip: false,
13            maxVel: {x: 100, y: 150},
14            friction: {x: 600, y: 0},
15            accelGround: 400,
16            accelAir: 200,
17            jump: 200,
18            init: function( x, y, settings ) {
19                this.parent( x, y, settings );
20                // Add the animations
21                this.addAnim( 'idle', 1, [0] );
22                this.addAnim( 'run', 0.07, [0,1,2,3,4,5] );
23                this.addAnim( 'jump', 1, [9] );
24                this.addAnim( 'fall', 0.4, [6,7] );
25            }
26        });
27    });

At this point, we are ready to switch back over to Weltmeister and add our player. When you load the editor back up, you should see our dorm1.js level. If it’s not there, simply load it up manually. When you load the level, the entities layer should automatically be highlighted. This layer works just like the other layers we created, so move over to the Canvas area and press the space bar to see the list of entities you can add to the level. Right now, you should see the player from the drop-down menu.

Select the player from the pop-up entity menu.
Figure 4-12. Select the player from the pop-up entity menu.

Select the player and add him to the level. You can place him anywhere for now; I put mine on the far left of the level. Also, make sure you hit Save once you are happy with your player’s start position.

A preview of the player in the level editor.
Figure 4-13. A preview of the player in the level editor.

It’s also important to note that as of version 1.19 of Impact, you no longer need to add each entity to your game’s requires block; it is now automatically handled for you when the level is loaded. Now you are ready to test out your game. Go to your browser and hit refresh.

The player is now in our game’s level.
Figure 4-14. The player is now in our game’s level.

You should now see your player in the game, but you will not be able to move him. Let’s fix that. Go back into the player.js class and add the following update() function:

update: function() {
    // move left or right
    var accel = this.standing ? this.accelGround : this.accelAir;
    if( ig.input.state('left') ) {
          this.accel.x = -accel;
          this.flip = true;
    }else if( ig.input.state('right') ) {
          this.accel.x = accel;
          this.flip = false;
    }else{
          this.accel.x = 0;
    }
    // jump
    if( this.standing && ig.input.pressed('jump') ) {
          this.vel.y = -this.jump;
    }
    // move!
    this.parent();
},

Note

As you continue adding code to your game, always make sure there is a comma to separate new functions, or you may get an error when you try to preview your code.

Now, you are ready to refresh the game and test out moving the player. As you can see, we can move our player, but he doesn’t animate or fall off ledges. We are going to need to set the gravity of the game. We can do this in main.js. Add the following property to that class:

MyGame = ig.Game.extend({
    gravity: 300,
    init: function() {

Now, if you go back to your game, you will be able to jump and fall off ledges. When you test it out, though, you will not have a clean-looking fall animation. Let’s add in some additional code to keep track of the player’s velocity in order to show the correct animation such as jump, fall, idle, and run. This should go below our jump code in the player.js class:

// set the current animation, based on the player's speed
if( this.vel.y < 0 ) {
    this.currentAnim = this.anims.jump;
}else if( this.vel.y > 0 ) {
    this.currentAnim = this.anims.fall;
}else if( this.vel.x != 0 ) {
    this.currentAnim = this.anims.run;
}else{
    this.currentAnim = this.anims.idle;
}

Now, we should be able to jump and run with corresponding animation, but there is one thing missing. We need a way to tell the player to flip his animation based on the direction he is running. We can do this by adding the following code just before the this.parent() call in the player.js update function:

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

Now we have a fully functional player. Let’s give it one more test and make sure everything works. At this point, our level is kind of boring—so let’s add a few monsters to the game.

Creating a Monster Class

Creating a monster is similar to creating a player. In fact, we are going to use the same basic class code but change its name and namespace. Create a new file called zombie.js in the entities folder.

Now, copy the following code into the monster class:

1    ig.module(
2        'game.entities.zombie'
3    )
4    .requires(
5        'impact.entity'
6    )
7    .defines(function(){
8        EntityZombie = ig.Entity.extend({
9
10        });
11    });

As you can see, we simply changed the entity name and class name, but everything else is the same as the code we used to start the player class. Now we are ready to add our monster’s animation and set its initial properties:

animSheet: new ig.AnimationSheet( 'media/zombie.png', 16, 16 ),
size: {x: 8, y:14},
offset: {x: 4, y: 2},
maxVel: {x: 100, y: 100},
flip: false,

Now we need to set up the animations just like we did for the player. This is a simple monster, so there are only a few sprites representing its animation. Let’s create a new init() method with the following code:

init: function( x, y, settings ) {
    this.parent( x, y, settings );
    this.addAnim('walk', .07, [0,1,2,3,4,5]);
},

With our default animation in place, we can start adding instances of the monster to test the level. Let’s switch over to Weltmeister, select the entities layer, and then add a monster by clicking into the layer and pressing the space bar, just as we did when adding the player. You can then click on the map to add the monster where you want it.

Select Zombie from the drop-down entity list.
Figure 4-15. Select Zombie from the drop-down entity list.

Feel free to add a few of them, as shown in Figure 4-16.

I’ve added two zombies to the level.
Figure 4-16. I’ve added two zombies to the level.

Once you have done this, refresh the game in your browser and you should see your new monsters. We haven’t added any movement logic yet, so they don’t do much right now. Let’s add some basic code to make them walk back and forth, but be smart enough not to fall off ledges. We’ll need to create an update function that will handle the basic movement logic or AI (Artificial Intelligence) for our monster:

update: function() {
    // near an edge? return!
    if( !ig.game.collisionMap.getTile(
          this.pos.x + (this.flip ? +4 : this.size.x −4),
               this.pos.y + this.size.y+1
          )
    ) {
          this.flip = !this.flip;
    }
    var xdir = this.flip ? −1 : 1;
    this.vel.x = this.speed * xdir;
    this.currentAnim.flip.x = this.flip;
    this.parent();
},

This function tests to see if the monster hits anything in the collision map. If it does, we toggle the value of the class flip property. After testing, the direction and velocity are updated before this.parent() is called. We will also need to define the monster’s friction and speed. You can add that toward the top of the class just under where we define the flip property:

friction: {x: 150, y: 0},
speed: 14,

Refresh the game to take a look at it in action. You will see the monster instances moving around, and when they hit the edge of a ledge, they flip and go the other way.

We want to make sure our zombies flip direction once they hit a wall or the end of a platform.
Figure 4-17. We want to make sure our zombies flip direction once they hit a wall or the end of a platform.

We just need to add a few more lines of code to clean this up. Add the following block of code to the end of your defines() function:

handleMovementTrace: function( res ) {
    this.parent( res );
    // collision with a wall? return!
    if( res.collision.x ) {
          this.flip = !this.flip;
    }
},

This helps make sure that if a monster runs into a wall, that it also turns around. Collisions with walls and the collision map are handled through the handleMovementTrace function. Now we have covered all our bases and made sure our zombies will not fall off ledges or platforms, but we still have one issue. There is no collision detection between the monster and the player.

The player simply passes through zombies without collision detection.
Figure 4-18. The player simply passes through zombies without collision detection.

Before we get into adding more code to the monster, we need to talk a little bit about entity-based collision detection in Impact.

So far, we’ve handled simple interactions with walls and platforms manually. However, Impact has built-in collision detection that we can use for interaction between our entities. That is, we can focus on setting up collision relationships instead of creating all that collision code from scratch. Let’s look a little closer at how we can use Impact to do this work for us.

Collision Detection

Since Impact has built-in collision detection, we can focus on setting up collision relationships instead of creating all the necessary code from scratch. Impact’s collision detection is based on bounding boxes. A bounding box is an imaginary rectangle around a sprite. If a sprite is 16×16 pixels, the box around it would be the same size. During a bounding box collision text, two entity’s boxes are overlapping. This kind of collision detection is incredibly fast and covers a good portion of the use cases you will probably need.

Note

It is important to note that one of the issues with bounding box collision is that it doesn’t take into account any transparent space around your sprite. This is why we had to tweak the size and offset values of our entities to help make our collision look as clean as possible.

Let’s take a look at how we can add collision detection to entities in our game.

type Property

The .type property allows us to group entities when doing collision detection. For example, you might assign all friendly entities to one group, and all enemy entities to another group. This way, you can set up your file so neither group will collide with their own types, but friendlies will collide with enemies, and vice versa. There are three .types in Impact that you can reference using their constant values:

ig.Entity.TYPE.NONE
ig.Entity.TYPE.A
ig.Entity.TYPE.B

By default, all entities are set to NONE. The other two groups are left open for your own needs. So, for instance, you can set all friendly entities to TYPE.A and hostile entities will check for collisions with TYPE.A only.

checkAgainst Property

The .checkAgainst property tells an entity which type property to check for when it collides with another entity. An entity can check for four types during the collision:

ig.Entity.TYPE.NONE
ig.Entity.TYPE.A
ig.Entity.TYPE.B
ig.Entity.TYPE.BOTH

The default value is always set to NONE. When two entities overlap, the .checkAgainst property of one entity is compared with the .type property of the other. If there is a match, the first entity’s check() method is called, and the latter object with which it collided is sent to the method as a parameter. You can customize the check() method to respond to such a collision

This example, which we’ll discuss in greater detail in a moment, shows damage applied after such a collision:

check: function( other ) {
    other.receiveDamage( 10, this );
}

collides Property

The final part of collision detection we need to learn about is the .collides property. This property determines how the entity collides with other entities. It’s important to note that this is independent of the collision map. This is strictly an entity-to-entity collision event. There are several types of collision property values:

ig.Entity.COLLIDES.NEVER
ig.Entity.COLLIDES.LITE
ig.Entity.COLLIDES.PASSIVE
ig.Entity.COLLIDES.ACTIVE
ig.Entity.COLLIDES.FIXED

By default, the collides property is set to NEVER, which ignores all collisions. FIXED is used for objects such as walls and platforms that won’t move as a result of a collision. It’s important to note that entities with a FIXED collides property may still move, just not when colliding with another entity. Elevators and moving platforms are good examples of this situation.

The remaining three collides values determine which entities move after a collision. If two ACTIVE entities collide, they will both move apart. The same is true when ACTIVE and PASSIVE entities collide, but the PASSIVE collides value exists so that entities of similar types can overlap without causing a resulting movement. So, when a PASSIVE entity collides with another PASSIVE entity, neither is moved by the collision.

Finally, where FIXED describes a “strong” entity that never moves away from a collision, LITE is used to specify a “weak” entity—one which always moves away from a collision.

Now that we have covered the collision properties entities have, let’s start setting up our own player and monster to have collision detection. Open up the player.js class and add the following properties:

type: ig.Entity.TYPE.A,
checkAgainst: ig.Entity.TYPE.NONE,
collides: ig.Entity.COLLIDES.PASSIVE,

Here, we are setting up all three collision properties for the player. We assign the player to TYPE.A, which will represent our friendly group. Next, we’ll set .checkAgainst to NONE. In our example, we’ll let the monster handle the collisions and, as shown in the previous section, apply damage to the player. Finally, we’ll set .collides to PASSIVE. This will prevent overlaps with another PASSIVE entity moving either entity as a result of the collision.

Now it’s time to set up our monster. Open up the zombie.js class and add the following:

type: ig.Entity.TYPE.B,
checkAgainst: ig.Entity.TYPE.A,
collides: ig.Entity.COLLIDES.PASSIVE,

We are setting our monster to the enemy group, which is TYPE.B. Since the player belongs to group TYPE.A, we will check against that group for collisions. And finally, we also set the enemy .collides property to PASSIVE. This will allow us to react when a collision is detected with the player, but because both entity .collides properties are set to PASSIVE, Impact won’t automatically move either of the players due to the collision.

If you tested your game now, it would appear that no collisions occur. This is because we set the collides property for all entities to PASSIVE, and Impact won’t adjust the position of either entity after a collision. We need to add some more code in the monster class to handle the collision when it is detected. Add the following method to the zombie.js class:

check: function( other ) {
    other.receiveDamage( 10, this );
}

You may recall during the discussion of the .checkAgainst property that this code applies damage to the entity the monster collides with. Remember that the colliding entity is passed to the function as an argument (other). This code executes Impact’s built-in receiveDamage() method in the player entity, and passes a value of 10, as well as a reference to the monster, to the player. The end result is that the player will lose all his health (10 points, by default).

Now, if you test the game, when the player hits a monster, he should be immediately killed. Visually, the player just disappears, since we haven’t created a death animation (see Figure 4-19.

The player is removed from the screen once the zombie kills him.
Figure 4-19. The player is removed from the screen once the zombie kills him.

Next, we will discuss health.

Health

Each entity has a health property. By default, this is set to 10. This value is incredibly useful if you are taking advantage of the built-in receiveDamage() method to subtract an entity’s health. To change an entity’s initial health, you can simply set this value in your class’s properties like so:

health: 20,

If we applied this to our player now, there would be no apparent change. Multiple collisions occur when passive entities overlap, because Impact doesn’t automatically resolve their positions and push the weaker entity away. In the next section, we’ll introduce weapons, and later you can tweak this value in the monster class to get a better result. For now we can just leave it as is.

Weapons

Right now, our player is defenseless. As soon as he hits a monster, he dies, and there is no way for the player to kill a monster. Well, that is about to change. One way to add a weapon is to create a new entity class. Weltmeister will then offer it as an option when placing entities on the map—convenient if you want to place weapons the player can pick up during the game. However, if your player will have access to the weapon throughout the game, you can keep Weltmeister’s menu options simple by creating an inner class within the player.js file.

Let’s find the player class’s closing blocks around line 59. Between the two closing block tags is where we will put our inner class:

EntityBullet = ig.Entity.extend({

});

Now you should have your bullet entity just before the end of the player.js class as shown in Figure 4-20.

Add the EntityBullet class just before the end of the player module.
Figure 4-20. Add the EntityBullet class just before the end of the player module.

Now we are ready to customize our player’s weapon.

As you can see, our EntityBullet is just like any other entity you have created. It extends ig.Entity, which means it has all the same inherited properties and methods as our player and monster. Basically, we are going to spawn a new bullet every time the player presses the fire button and, based on which way the player is facing, the bullet entity will move in that direction. When the bullet hits a wall or monster, it will remove itself and, in the case of a monster, apply damage. Let’s start by adding a few properties to our bullet:

size: {x: 5, y: 3},
animSheet: new ig.AnimationSheet( 'media/bullet.png', 5, 3 ),
maxVel: {x: 200, y: 0},

This will set up our bullet’s size, graphic, and maximum velocity. It’s important to note that our bullets don’t have y velocity since they only move horizontally. Also, we need to make sure our bullet can move faster than the player. We don’t want to fire our gun and run into or beyond our bullet as it flies through the air. Next, we will need to set up some collision information for the bullet:

type: ig.Entity.TYPE.NONE,
checkAgainst: ig.Entity.TYPE.B,
collides: ig.Entity.COLLIDES.PASSIVE,

As you can see, we are going to have our bullet test for TYPE.B entities, and its collides property is set to passive so it doesn’t displace entities it collides with. Now we can add our init() method:

init: function( x, y, settings ) {
    this.parent( x + (settings.flip ? −4 : 8) , y+8, settings );
    this.vel.x = this.accel.x = (settings.flip ? -this.maxVel.x : this.maxVel.x);
    this.addAnim( 'idle', 0.2, [0] );
},

Here we are taking the flip value that will be passed into the EntityBullet via the optional settings object and applying an offset to the x,y values we pass to the parent method. This ensures that the bullet starts in the correct position and appears to be fired from the gun. Next, we set the velocity and acceleration x value to our maximum velocity x value. If the player is facing left, make this negative. This forces the bullet to fire at its maximum speed instead of slowly accelerating toward its maximum velocity.

Now we need to test for our collisions. Let’s start by reacting any time the bullet hits something in the collision layer:

handleMovementTrace: function( res ) {
    this.parent( res );
    if( res.collision.x || res.collision.y ){
        this.kill();
    }
},

The handleMovementTrace() gets called while an entity is moving. This method is associated with the collision map, so we can detect when an entity hits a wall. We check the res object parameter if a collision happens on the x or y values.

check: function( other ) {
    other.receiveDamage( 3, this );
    this.kill();
}

All we need to do now is add some code to our player in order to fire the bullets.

Firing the Weapon

Since inner classes are just like any other class we would create in Impact, we can simply use the ig.game built-in spawnEntity() method to create a new instance of the bullet when the player presses the fire key. Our player and monster are created during the level parsing process, so we have not had to manually instantiate an entity yet. The spawnEntity() function helps ensure that when we create a new entity, it gets added to Impact’s render list. Open up your player.js class and put the following code under the jump logic in the update method:

// shoot
if( ig.input.pressed('shoot') ) {
    ig.game.spawnEntity( EntityBullet, this.pos.x, this.pos.y, {flip:this.flip} );
}

As you can see, we are going to look for the shoot event, which we bound to the C key in our main class. This should be very straightforward—we tell ig.game that we are going to spawn a new entity. The spawnEntity() method needs a reference to the class we want to create and its starting x,y position, along with any additional settings we want to pass to the new entity. Notice here that we create a generic object with a property called flip with the player’s flip value. This is what tells the bullet which direction it should be fired.

At this point, we can test that our gun works by refreshing the game in the browser and hitting C. So, now you should be able to fire your weapon and kill the monsters.

You should see bullets being fired when you press the C key.
Figure 4-21. You should see bullets being fired when you press the C key.

At this point, the monsters will die after a few shots. If you change the monsters’ life property to something lower, it will take less shots to kill them. This is because on every collision the bullet detects with an enemy, it calls receivedDamage() and passes in 3 as the value. Likewise, you can make the bullets stronger by changing the amount of damage they apply.

Right now, our gun is kind of boring. Let’s add another weapon to the mix and see what happens.

Add Multiple Weapons

So, we built a basic gun that fires bullets, but what about adding something a little more exciting? How about a grenade that bounces and explodes when it hits stuff? We can easily add in new types of weapons just like we did with our bullet. Let’s set up the beginning of our grenade class after our EntityBullet. Add the following inner class to your player.js module:

EntityGrenade = ig.Entity.extend({

});

Just like with our bullet, we are ready to customize the grenade’s properties. We will need to give it a graphic and set the size and offset:

size: {x: 4, y: 4},
offset: {x: 2, y: 2},
animSheet: new ig.AnimationSheet( 'media/grenade.png', 8, 8 ),

Now let’s set up our collision detection:

type: ig.Entity.TYPE.NONE,
checkAgainst: ig.Entity.TYPE.BOTH,
collides: ig.Entity.COLLIDES.PASSIVE,

Pay special attention to the fact that we are setting checkAgainst to TYPE.BOTH. What this means is that our grenade can collide with our zombie and our player. You’ll see how this works when we are ready to test our grenade later on. However, in order for our grenade to move and bounce, we will need to add a few additional properties. Add the following to your grenade class:

maxVel: {x: 200, y: 200},
bounciness: 0.6,
bounceCounter: 0,

Here, we are setting our grenade’s maximum velocity. Also, we are going to keep track of how many times it will bounce before blowing up, as well as its bounciness value. You can tweak these values once we enable the player to actually fire the grenade, so you can test what effect the bounciness value will have.

Let’s override the init() method with the following code:

init: function( x, y, settings ) {
    this.parent( x + (settings.flip ? −4 : 7), y, settings );
    this.vel.x = (settings.flip ? -this.maxVel.x : this.maxVel.x);
    this.vel.y = -(50 + (Math.random()*100));
    this.addAnim( 'idle', 0.2, [0,1] );
},

Here, we are going to determine the beginning x velocity based on a flip parameter that will be passed in via the settings object. Just like with our bullet, when the player fires the grenade we will set the player’s own flip value into a property of the settings object so that we know what direction to fire the grenade. It’s also incredibly important that we get the start x,y position offset correct. Since the grenade can collide with the player, we wouldn’t want it to fire the weapon and instantly blow up.

Next, we offset the y velocity by negative 50 plus a random number that ranges from 0 to 100, which will help add an arc to the grenade when it gets fired. The randomness makes sure that the player throws the grenade slightly differently each time. This, along with the fact that the grenade can also kill the player, will help balance the fact that this is a more powerful weapon. After that, we just set the idle animation to display sprites 0 and 1, which will loop through images of the grenade rotating as it flies through the air.

We are getting very close to testing out our grenade, but before we do, we’ll have to override the handleMovementTrace() method and write some logic to handle collisions, track the number of bounces, and remove the grenade from the display if it bounces too many times:

handleMovementTrace: function( res ) {
    this.parent( res );
    if( res.collision.x || res.collision.y ) {
          // only bounce 3 times
          this.bounceCounter++;
          if( this.bounceCounter > 3 ) {
               this.kill();
          }
    }
},

This works exactly like our bullet, except that when a collision is detected, we increment the .bounceCounter by 1. If the .bounceCounter is greater than 3, we kill the grenade. We will talk more about the entity’s kill() method later.

Now that we can handle tracking bounces, let’s add logic when the grenade collides with an enemy. Like we did before in the EntityBullet class, we are going to override the check function, which gets called when a .checkAgainst group has been detected. Add the following function to your grenade class:

check: function( other ) {
    other.receiveDamage( 10, this );
    this.kill();
}

Finally, we have increased the damage of the grenade so that it kills anything in a single hit. Now, all we need to do in order to have the player toggle between weapons is bind a new key to toggle between the weapons and then have the player swap between the correct one. Let’s add the following bind logic into our main.js init() function:

ig.input.bind( ig.KEY.TAB, 'switch' );

Now, if we go back into our player class, we will need a way to keep track of the current weapon. Add the following property to the beginning of our EntityPlayer class in the player.js file:

weapon: 0,
totalWeapons: 2,

From there, we can add a simple test to see when the player presses the weapon toggle button, so we can toggle the weapon property. Add the following code below where we test if the shoot button was pressed in the PlayerEntity update() method:

if( ig.input.pressed('switch') ) {
    this.weapon ++;
    if(this.weapon >= this.totalWeapons)
          this.weapon = 0;
    switch(this.weapon){          case(0):
               this.activeWeapon = "EntityBullet";
               break;
          case(1):
               this.activeWeapon = "EntityGrenade";
          break;
    }
}

Now, when we go to spawn our weapon instance, we can simply check to see which weapon is set in the weapon property and spawn the correct instance.

We will also need to add a new property called this.activeWeapon to the top of our class:

activeWeapon: "EntityBullet",

Notice how we have to set the value to a string instead of a reference to the class itself? This will be evaluated correctly when the player class gets created. If we don’t make the default value a string, it will break the player class when it tries to load.

The last thing we need to do is update our shoot code in the player class with the following:

if( ig.input.pressed('shoot') ) {
    ig.game.spawnEntity( this.activeWeapon, this.pos.x, this.pos.y,
        {flip:this.flip} );
}

This simply spawns a new instance of any class you have set as the this.activeWeapon value. Now you are ready to test out your new grenade and switch between the two weapons.

The player can now throw grenades.
Figure 4-22. The player can now throw grenades.

You may have noticed that the player still looks like he has a gun in his hand and that there really aren’t any visual changes when you switch weapons. We can quickly fix this by modifying how we set up our player’s animations in the init() method. Go ahead and delete the four lines of code where we set up the idle, run, jump, and fall animations. Then add the following in its place:

this.setupAnimation(this.weapon);

Now we will create a new method called setupAnimation(), which takes our current weapon ID as an offset. Here is the code to add below the init() method:

setupAnimation: function(offset){
  offset = offset * 10;
  this.addAnim('idle', 1, [0+offset]);
  this.addAnim('run', .07, [0+offset,1+offset,2+offset,3+offset,4+offset,
      5+offset]);
  this.addAnim('jump', 1, [9+offset]);
  this.addAnim('fall', 0.4, [6+offset,7+offset]);
},

This is just like our original animation setup, except we now take the weapon ID (which becomes an offset), multiply it by the total number of player frames with a weapon, and add it to each animation. Let’s look at the player sprite sheet so you can see what’s going on.

The player sprites with a gun and without one.
Figure 4-23. The player sprites with a gun and without one.

As you can see, we have 10 sprites holding a gun and 10 sprites without the gun. By offsetting the animation by 10 frames, we can easily switch between the different sets of sprites. This is a common trick, and one we will use later on when we add death animations. For now, we just need to add one last line of code to help update the player graphics when we switch weapons. Add a call to this.setupAnimation() at the end of where we test for the weapon switch key press:

switch(this.weapon){
    case(0):
          this.activeWeapon = "EntityBullet";
          break;
    case(1):
          this.activeWeapon = "EntityGrenade";
    break;
}
this.setupAnimation(this.weapon);

Now when you test and hit the Tab key, you should see the player’s animation change based on the weapon he is using (Figure 4-24).

Now you can see the player’s sprite update when switching between weapons.
Figure 4-24. Now you can see the player’s sprite update when switching between weapons.

From here, you should be able to add even more weapons to your game by simply using the above pattern and building upon it.

Killing Entities

You may have noticed while we set up our weapons that we called a built-in method called kill(). While reducing an entity’s life will destroy it and automatically call kill() for you, there are times when you may need to do this manually—like when the grenade collides with an enemy or it bounces too much. This method actually completely removes the entity from the render list, so it is a helpful way to permanently remove entities from the game. If you do not call kill() on anything you need to remove from the game, things will start to slow down considerably, so make sure you take advantage of the kill() method.

Respawning the Player

Since the player dies as soon as he collides with an enemy, we should add some logic to respawn the player. The easiest way to do this is to save out the start position of the player when he gets created so we can restore him to the same position when he dies. Let’s add the following property to our PlayerEntity class:

startPosition: null,

Then, we can store the initial position of the player by adding the following to the init() method above the call to this.parent():

this.startPosition = {x:x,y:y};

What this does is save a generic object with the x,y position that gets passed into the constructor of our player class. Now, we just need to override the kill() method to this:

kill: function(){
    this.parent();
    ig.game.spawnEntity( EntityPlayer, this.startPosition.x, 
        this.startPosition.y );
}

So, what will happen is that when kill() gets called after the player collides with a monster, we call this.parent(), which will properly remove the player instance from the game. Then, we immediately spawn a new player at the saved startPosition. Right now, it is a little jarring, but you could easily add a delay and then respawn after displaying some message to the user.

Another really cool trick about this approach is that since we saved the initial x,y position of the player in the startPosition property, we could easily update this value if the player walks through a checkpoint. This means that we don’t need any complex logic to continually respawn the player throughout the level. All the logic is contained inside the player instance itself.

One thing you should pay special attention to is when a monster is on top of the respawn position. Since we don’t reset the level, there is a chance that we could lock up the game if the monster kills the player as soon as he respawns, as shown in Figure 4-25.

Right now, a monster on our respawn position will lock up the game.
Figure 4-25. Right now, a monster on our respawn position will lock up the game.

The same thing can happen if there are a lot of grenades bouncing around where the player respawns. Usually, games offer some sort of invincibility mode when the player restarts. Here is a quick example of how to do that. Start by adding the following two properties to our EntityPlayer class:

invincible: true,
invincibleDelay: 2,
invincibleTimer:null,

This will allow us to tell if the player is invincible, and also for how long. Next, we will need to add the following method to handle toggling the invincibility:

makeInvincible: function(){
    this.invincible = true;
    this.invincibleTimer.reset();
},

This will allow us to call makeInvincible() on the player at any time, and we can reset the invincibleTimer as well as toggle the invincible flag. Now we are going to have to override our receiveDamage() and draw() methods:

receiveDamage: function(amount, from){
    if(this.invincible)
        return;
    this.parent(amount, from);
},
draw: function(){
    if(this.invincible)
        this.currentAnim.alpha = 
            this.invincibleTimer.delta()/this.invincibleDelay * 1 ;
    this.parent();
}

In the receiveDamage() method, we are using a guard clause to test if invincibility has been toggled and, if so, we just exit the method and don’t apply any damage. In the draw method, we also test for invincibility and, if it is activated, we are going to set the alpha value of the sprite to reflect how much longer they are invincible. We start at 0 and they will slowly fade into the game. Alpha in Impact is a value between 0 and 1. We can easily find a percentage of that value by dividing the invincibleTimer’s delta by the invincibleDelay. By multiplying it by 1, the total value of alpha, we get a percentage that we can use to make the player fade in.

Before we can test this, we need to do two more things. First, we need to add the following to our init() method:

this.invincibleTimer = new ig.Timer();
this.makeInvincible();

Next, we need to add the following code to our update() method just before our call to this.parent():

if( this.invincibleTimer.delta() > this.invincibleDelay ) {
    this.invincible = false;
    this.currentAnim.alpha = 1;
}

This basically tests to see if our timer is greater than the delay we defined. Once that happens, we disable invincibility by setting invincible to false and forcing the alpha to be 1. If you refresh, you should now see the player fade in when he is created and after you respawn.

When a new player is spawned, he is temporarily invincible.
Figure 4-26. When a new player is spawned, he is temporarily invincible.

This should fix the issue we had before (when the player respawns on top of a monster or grenade) so that we don’t lock up the game. Also, because of the way this was set up, you can now call makeInvincible() at any time if you wanted to give the player a power-up or show that the player has taken damage without actually respawning him.

Create Death Animations

One of the easiest ways to show a death animation is to create a small particle explosion where the player is killed. Not only does this cut down on the amount of animations you have to create but you can also use the same technique to show damage taken by a projectile weapon. In order to do this, we will need to create two new entities, one for the explosion and the other for the actual particles. Let’s add the following inner class to our player.js module:

EntityDeathExplosion = ig.Entity.extend({
    lifetime: 1,
    callBack: null,
    particles: 25,
    init: function( x, y, settings ) {
          this.parent( x, y, settings );
          for(var i = 0; i < this.particles; i++)
               ig.game.spawnEntity(EntityDeathExplosionParticle, x, y,
               {colorOffset: settings.colorOffset ? settings.colorOffset : 0});
               this.idleTimer = new ig.Timer();
    },
    update: function() {
          if( this.idleTimer.delta() > this.lifetime ) {
               this.kill();
               if(this.callBack)
                     this.callBack();
               return;
          }
    }
});

This is a very simple class. It handles spawning particle entities, which we will create next, and also has a timer, which we use to call a callback() method that is supplied by the setting property. Pay special attention to this.idleTimer and the new ig.Timer(). We use these to keep track of how much time has elapsed since its instantiation, just like we did when we added invincibility to the player.

There is also something else going on here. You may have noticed that when we spawn our EntityDeathExplosionParticle, we are passing in a color offset value. If you take a look at the blood sprite, you will see that we have colored sprites for the player in red and for the zombie in green.

This sprite contains the player and zombie blood particles.
Figure 4-27. This sprite contains the player and zombie blood particles.

This is a neat trick and one that is used in a lot of sprite sheet-based games. Our blood particles are going to be 2×2 pixels in size. That means we have eight sprites for each color. When we set up our particle, we will apply the color offset to the graphic we display. So if the player is hit, we will add 0 to the offset, which will generate a random red color. For zombies we will add 1 to the offset, which will multiply by the base number of possible sprites and move the randomly selected sprite into the green zone. Let’s take a look at our particle class to see this in action. Create a new inner class with the following code:

EntityDeathExplosionParticle = ig.Entity.extend({
    size: {x: 2, y: 2},
    maxVel: {x: 160, y: 200},
    lifetime: 2,
    fadetime: 1,
    bounciness: 0,
    vel: {x: 100, y: 30},
    friction: {x:100, y: 0},
    collides: ig.Entity.COLLIDES.LITE,
    colorOffset: 0,
    totalColors: 7,
    animSheet: new ig.AnimationSheet( 'media/blood.png', 2, 2 ),
    init: function( x, y, settings ) {
        this.parent( x, y, settings );
        var frameID = Math.round(Math.random()*this.totalColors) + 
        (this.colorOffset * (this.totalColors+1));
        this.addAnim( 'idle', 0.2, [frameID] );
        this.vel.x = (Math.random() * 2 - 1) * this.vel.x;
        this.vel.y = (Math.random() * 2 - 1) * this.vel.y;
        this.idleTimer = new ig.Timer();
    },
    update: function() {
        if( this.idleTimer.delta() > this.lifetime ) {
            this.kill();
            return;
        }
        this.currentAnim.alpha = this.idleTimer.delta().map(
            this.lifetime - this.fadetime, this.lifetime,
            1, 0
        );
        this.parent();
    }
});

As you can see, the particle has a few properties such as its maximum velocity, how long before it fades away, bounciness, and initial velocity. Most of this should look very familiar from what we did with our grenade class. As you can see in the init() method, we assign a random value to the particle’s vel.x and vel.y values, which sends each one off in different directions. Since this is blood and we don’t want it bouncing around like the grenade, the bounciness property is set to 0. We take advantage of this.currentAnim.alpha value, which assigns a new alpha value after each update, and eventually the particle disappears. Once it fades away, we call kill() to remove it from the display.

Now that we have our particle emitter and our particle, we can extend the player’s kill() method to spawn our EntityDeathExplosion where the player was killed and watch it spawn random particles as if the player exploded. Here is the modified EntityPlayer kill() method:

kill: function(){
    this.parent();
    var x = this.startPosition.x;
    var y = this.startPosition.y;
    ig.game.spawnEntity(EntityDeathExplosion, this.pos.x, this.pos.y,
    {callBack:function(){ig.game.spawnEntity( EntityPlayer, x, y)}} );
}

Since we are passing a function into the settings object, we will need to re-scope the start position of the player. If you refresh your browser and run the player into the monster, you will now see him explode into tiny pieces that bounce and fade away.

The player now explodes into pieces.
Figure 4-28. The player now explodes into pieces.

As I mentioned before, this is also a great effect for us to show when an entity has been hit. Let’s override the zombie.js receiveDamage() method with the following:

receiveDamage: function(value){
    this.parent(value);
    if(this.health > 0)
          ig.game.spawnEntity(EntityDeathExplosion, this.pos.x, this.pos.y, 
          {particles: 2, colorOffset: 1});
},

We can actually use the same death explosion class in our zombie entity, even though it is an inner class of player. This is a neat little hack thanks to the fact that JS’s scope is global and, when any entity gets defined in Impact, it is available throughout the game engine. So, in the EntityZombie class, we simply spawn a new death explosion just like we did in the player class, but pass in a smaller number of particles to be emitted. We also pass in the colorOffset so that we can display green blood instead of red. Now when a bullet hits the zombie, little particles will shoot off of it. Also, don’t forget to use the same death animation technique we used on the player by overriding the zombie.js kill() method with the following:

kill: function(){
    this.parent();
    ig.game.spawnEntity(EntityDeathExplosion, this.pos.x, this.pos.y, 
    {colorOffset: 1});
}

And there you go; you have just created a nice-looking dynamic death animation for your player and monster. We can also apply the same technique to our grenades and make their explosions more visually appealing, so let’s take a look.

Adding Grenade Explosions

Now that we have seen how to add death animations to our player and zombie, let’s look at how to make our grenades explode. Let’s add the following particle to our player.js module:

EntityGrenadeParticle = ig.Entity.extend({
    size: {x: 1, y: 1},
    maxVel: {x: 160, y: 200},
    lifetime: 1,
    fadetime: 1,
    bounciness: 0.3,
    vel: {x: 40, y: 50},
    friction: {x:20, y: 20},
    checkAgainst: ig.Entity.TYPE.B,
    collides: ig.Entity.COLLIDES.LITE,
    animSheet: new ig.AnimationSheet( 'media/explosion.png', 1, 1 ),
    init: function( x, y, settings ) {
        this.parent( x, y, settings );
        this.vel.x = (Math.random() * 4 - 1) * this.vel.x;
        this.vel.y = (Math.random() * 10 - 1) * this.vel.y;
        this.idleTimer = new ig.Timer();
        var frameID = Math.round(Math.random()*7);
        this.addAnim( 'idle', 0.2, [frameID] );
    },
    update: function() {
        if( this.idleTimer.delta() > this.lifetime ) {
            this.kill();
            return;
        }
        this.currentAnim.alpha = this.idleTimer.delta().map(
            this.lifetime - this.fadetime, this.lifetime,
            1, 0
        );
        this.parent();
    }
});

At this point, everything should look very familiar. We probably could have even extended our EntityDeathExplosionParticle but, to keep things simple, I just copied over the code and changed a few properties. Now we just need to spawn a few particles once the grenade explodes. Override the EntityGrenade kill() method with this code:

kill: function(){
    for(var i = 0; i < 20; i++)
        ig.game.spawnEntity(EntityGrenadeParticle, this.pos.x, this.pos.y);
    this.parent();
}

Refresh the game and fire a grenade. You should see a nice little particle explosion when it collides with anything or bounces too many times.

The grenade now explodes.
Figure 4-29. The grenade now explodes.

Customizing the Camera

Right now, our level is really boring. Impact was designed for side-scrolling games, so let’s go back into our map editor and extend out the level so the player has some room to run around. Create an opening in the far right wall and add another room to the map. Make sure that you increase the size of the main layer and the collision layer as well.

The level after expanding it.
Figure 4-30. The level after expanding it.

Once you have extended the level, save and try to play it. You may notice something isn’t quite right (Figure 4-31).

As you move through the level, the camera doesn’t follow the player.
Figure 4-31. As you move through the level, the camera doesn’t follow the player.

Did you see that the game’s camera is not following the player? We will need to set this up manually in the main.js class. Open it up and we will override the update function with the following code:

update: function() {
    // screen follows the player
    var player = this.getEntitiesByType( EntityPlayer )[0];
    if( player ) {
          this.screen.x = player.pos.x - ig.system.width/2;
          this.screen.y = player.pos.y - ig.system.height/2;
    }
    // Update all entities and BackgroundMaps
    this.parent();
},

The way that this code works is that we take advantage of a method of the game class called getEntitiesByType(). This is a very important API when it comes to finding instances of entities in your game. Because we know that there is only a single instance of our player, we can explicitly look for it. There are better ways of getting a reference to the player, but for now we will just use this technique to keep things simple.

After we see if the player exists, we can get the screen resolution and player position to center the screen’s x,y values. By setting the screen.x and screen.y values, the renderer will automatically adjust the camera to that position. You can also do a lot of cool tricks with this, like easing the camera movement or limiting it so it doesn’t scroll offscreen.

Now, refresh the game and you should see that the camera now follows our player as it moves through the level.

The camera following the player through the level.
Figure 4-32. The camera following the player through the level.

So, now that we have added the ability to move our camera around the level, it’s time to allow the player to exit this level.

Loading New Levels

It looks like we are ready to load our next level. Loading levels in Impact is incredibly easy; we actually did it as one of the first steps in setting up this game. In this section, I will talk about building something we call a trigger, which is an invisible area of the map that executes an activity when the player enters it. In this case, we will be building a level exit.

Let’s start by creating a new entity file called levelexit.js and add the following code to it:

12    ig.module(
13        'game.entities.levelexit'
14    )
15    .requires(
16        'impact.entity'
17    )
18    .defines(function(){
19        EntityLevelexit = ig.Entity.extend({
20
21        });
22    });

Since our exit doesn’t have any graphics, we still need something to display in Weltmeister. Let’s add the following properties to our class:

_wmDrawBox: true,
_wmBoxColor: 'rgba(0, 0, 255, 0.7)',
size: {x: 8, y: 8},

These two properties with the _wm prefix tell Weltmeister how to render the object in the edit view, even though it doesn’t have an actual graphic in the game. So, Weltmeister will draw an 8×8 pixel blue box.

Now, we need a way to store the name of the next level we should load when the player collides with the level exit entity. Add the following property, which will be automatically set during our entity’s construction by the settings parameter we will pass in from the level data:

level: null,

Next, we just need to add some collision code. Let’s have our level exit check against any TYPE.A entities by adding the following property to the top of your class:

checkAgainst: ig.Entity.TYPE.A,

Now, we will override update() and remove its call to this.parent() so we are not spending render cycles trying to draw an entity with no graphics. This is a great technique for any kind of triggers you may build for your map that aren’t required to be updated visually on every frame:

update: function(){},

Finally, we will also override the check() method to handle the collision:

check: function( other ) {
    if(other instanceof EntityPlayer){
          if( this.level ) {
               var levelName = this.level.replace(/^(Level)?(\w)(\w*)/,
                   function( m, l, a, b ) {
               return a.toUpperCase() + b;
          });
               ig.game.loadLevelDeferred( ig.global['Level'+levelName] );
          }
    }
}

As you can see, we simply test that the instance of other (which is passed into the method during a collision) is an instance of the player’s class. This helps avoid any other entity of TYPE.A you may have accidentally triggered in the level exit.

The last part of the code simply does a regex cleanup of the exit’s level property to make sure it is capitalized correctly before calling ig.game.loadLevelDeferred(). This method is very important. You may remember that in our main class, it simply called loadLevel(). Well, loadLevelDeferred() waits until the main game’s update loop is completed before loading the level. This will help avoid any sudden redraw errors that may happen when trying to exit in the middle of the render loop.

Once we have our new EntityLevelexit, we can open up Weltmeister and create a small exit to place our entity in.

Now we can see where we placed the level exit in the editor.
Figure 4-33. Now we can see where we placed the level exit in the editor.

When you add the level exit, you will need to tell it what level to load. Click on it and go to the layer area just under where it says Entity Settings. You should see all the properties of your entity instance. For the Key, put level and in Value, put dorm2.

Setting up a level property on the LevelExit entity.
Figure 4-34. Setting up a level property on the LevelExit entity.

It is very important that you hit Enter/Return after adding a value to an entity’s Key, or it will not be saved. You will know it has been saved when you see the new Key/Value listed under the entity’s name and its x,y values, which are set up by default. You can modify any Key by clicking on it.

Modifying entity properties in the level editor.
Figure 4-35. Modifying entity properties in the level editor.

Now that our entity is configured, we need to create a new level. Name your level dorm2 and design it as shown in Figure 4-36.

The second level of our game.
Figure 4-36. The second level of our game.

As you build out your new level you may notice that the player graphic is missing in the editor. This happens because now our player is set to invincible when he is created, so he is invisible. If this is an issue, you can add the following two properties to your player.js class:

_wmDrawBox: true,
_wmBoxColor: 'rgba(255, 0, 0, 0.7)',

This will render out a red box for the player when in the editor, just like we did with our level edit.

We use a red box to display the invisible player in the level editor.
Figure 4-37. We use a red box to display the invisible player in the level editor.

The last thing you need to do is add the level and the level exit to the main.js requires block:

'game.levels.dorm2,

Once you have done this, refresh your game and you should be able to exit the level and go into the second level.

You may notice that the transition is a little jarring. There are a few things you can do to make sure that doesn’t happen, such as matching the level exit and spawn points up, or building a quick transition before exiting the level. Usually, games have an end-of-level summary screen that gets displayed, so that when the next level loads up the player doesn’t notice the transition as much. We’ll talk more about this later in the book.

Get Building HTML5 Games with ImpactJS now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.