[Tutorial]Building a Simple TileMap Game, Part 5 of 9

Adding Enemies

Now the fun part – adding enemies!

Download the code in the repo https://github.com/filmote/Tilemap_3 and run it.

As you can see, there are now three enemies who will chase you around the small world – if they touch you its game over. You will also notice that the enemies can only move on the green tiles just like the player. The following code shows how to use the previously defined checkMovement() function to test enemy movement as well.

But before we get there, let’s have a quick look at the changes I have made to the entity class we used previously. In the code below, we have a base struct called Entity and two separate classes, Player and Enemy, that extend it. This simple model allows us to have specific methods for the player class to assist in calculating positions within the world for collision detection and so forth. Currently the Enemy and Player struct simply extend the base Entity class but these could be altered later to capture details such as health, inventory, etc.


struct Entity {
    
    int16_t x;
    int16_t y;

    const uint8_t width = 12;
    const uint8_t height = 15;

};


struct Player : Entity { };

struct Enemy : Entity { };

Finally, we can declare our player and array of enemies:

Player player;
Enemy enemies[Constants::numberOfEnemies];

In this simple example game, the enemies will try to move towards the player but at the same time avoiding obstacles they cannot move through. This logic is encapsulated in the function shown below.

The position of the player is compared to the current location of each enemy in the array. If the player is to the left of the enemy and the enemy can move in that direction (determined using the existing checkMovement() function) then the player is moved. Depending on the position of the enemy relative to the player, the enemy may move both horizontally and vertically.

The code has been abbreviated and only shows the logic for the left movement. The three other directions contain similar logic and can be reviewed in the source code downloaded earlier.

void handleEnemyMovements() {
    
    
    // Move each enemy individually ..
    
    for (uint8_t i = 0; i < Constants::numberOfEnemies; i++) {

        // Move left?

        if (player.x < enemies[i].x) {

            if (checkMovement(enemies[i], enemies[i].x - 1, enemies[i].y, 
                              Direction::Left)) {
                enemies[i].x--;
            }
            
        }
        
        …
        
    }

}

Finally, detecting a collision between an enemy and player is achieved by testing whether the player ‘rectangle’ overlaps the enemy ‘rectangle’ in any way. This logic for this is shown below ..

bool collide(Player &player, Enemy &enemy) {

return !(enemy.x                 >= player.x() + player.width   ||
          enemy.x + enemy.width  <= player.x()                  ||
          enemy.y                >= player.y() + player.height  ||
          enemy.y + enemy.height <= player.y());

}



<< Prev | Next >>

Part 1 of 9: Building a Simple Tilemap Game
Part 2 of 9: Moving a Player Around the World
Part 3 of 9: Rendering the Viewport and Player
Part 4 of 9: Detecting Obstacles
Part 5 of 9: Adding Enemies
Part 6 of 9: Using FemtoIDE and Piskel
Part 7 of 9: Doors and Inventory Items
Part 8 of 9: Multiple Worlds
Part 9 of 9: Sixteen Tiles Only?

2 Likes