In this section, we will outline the steps taken to implement a number of animated enemy sprites that the player must attempt to avoid in order to stay alive. We will also look at the steps taken to implement a simplistic form of artificial intelligence known as patrolling. By utilizing this technique, we can give life to each of the enemies by allowing them to move freely around the level. This will involve the implementation of an Enemy
object as well as adjusting the Level
and Main
objects to handle the newly created Enemy
objects.
To begin with, we will implement the
Enemy
object, which will be responsible for creating and drawing enemies to the canvas. We will also be updating the enemy's position and performing collision detection.function Enemy() { this.velocity = 30; this.InitEnemy = function(texture, x, y, z, frameCount, frameRate) { this.InitAnimationManager(texture, x, y, z, frameCount, frameRate); return this; } this.DisposeEnemy = function() { this.DisposeAnimationManager(); } this.Update = function(deltaTime, context, deltaX, deltaY) { this.x -= this.velocity * deltaTime; if(this.BoundingBox().Intersects(player.BoundingBox())) { this.DisposeEnemy(); } } } Enemy.prototype = new AnimationManager;
The
Enemy
object makes use of a function known asIntersects
, which determines if two rectangles have collided. In order to utilize this function, we will need to implement a new object known asRectangle
.function Rectangle() { this.left = 0; this.top = 0; this.width = 0; this.height = 0; this.InitRectangle = function(left, top, width, height) { this.left = left; this.top = top; this.width = width; this.height = height; return this; } this.Intersects = function(rect) { if(this.left + this.width < rect.left) return false; if(this.top + this.height < rect.top) return false; if(this.left > rect.left + rect.width) return false; if(this.top > rect.top + rect.height) return false; return true; } }
For our next step, we will need to create a new instance of the
Enemy
object inside of theLevel
object, and we also need to position an enemy unit within theLevel
constructor.this.enemy = new Object; this.tileWidth = 0; this.tileHeight = 0; this.InitLevel = function(width, height) { this.tileWidth = tile.width; this.tileHeight = tile.height; for(var i = 0; i < 50; i++) { this.tiles[i] = 1; } this.enemy['10'] = 'Enemy'; this.AddTile(width, height); this.AddEnemy(width, height); return this; }
Now that we have an enemy positioned within our level, we need to add that enemy unit to the level. This is done through means of the
AddEnemy
function, which is as follows:var x, y; this.AddEnemy = function(width, height) { for(var i = 0; i < this.tiles.length; ++i) { if(this.enemy[i]){ x = i * this.tileWidth + this.tileWidth / 2; y = height - this.TerrainHeight(i); if(this.enemy[i] == 'Enemy') { new Enemy().InitEnemy(enemy_left, x - enemy_left.width / 2, y - enemy_left.height, 7, 4, 4); } } } }
The final part that is required is to load the enemy sprite sheet into the application inside of the
Main
object.var enemy_left = new Image(); enemy_left.src = "textures/enemy_left.png";
The Enemy object creates a new enemy object that represents an animated object similar to the player object. The Enemy
object constructor takes in a texture parameter, as well as a 2D position, depth position, frame count, and frame rate parameters. The texture is a 2D sprite sheet, which is used to animate the enemy. The x, y, and z positions represent the position of the enemy on the canvas as well as the position of the enemy texture within the list of layers within the game. The frame count is used to determine how many frames make up the enemy sprite sheet and finally the frame rate dictates how many frames the enemy texture should be played each second.
The update
function within the Enemy
object is used to move an enemy object along the x axis and towards the player. The update
function is also used to check for any collisions between the player and the enemy. If there is a collision then the enemy object that collided with the player is removed from the game and the player has part of their health deducted. Finally we use the prototype keyword to extend the functionality of the AnimationManager
to the Enemy
object.
This collision detection makes use of the Rectangle
object, which checks whether or not two rectangles are overlapping and if so whether a collision has occurred.
Inside of the Level
object, we declare and initialize a new enemy object array. Inside of the Level
constructor, we then assign a position for a new enemy object to be drawn to.
This position is then stored inside of the enemy object array and passed into the AddEnemy
function. This function loops through each tile within the levels tile array and then loops through the enemy object array and places the enemy at the position above the tile that it corresponds to.
The final code extract refers to loading the enemy sprite sheet texture into the application. This is done exactly as we previously did for each of the textures within the game. A new enemy image object was created within the Main
object and an external path that shows the location of the enemy sprite sheet texture was passed to the image object in question.