Zombie splat – creating random zombies


Code for this tutorial is on this git branch:

git clone -b zombies https://dglencross@bitbucket.org/dglencross/zombiegametutorial.git

Have a look here for help setting it up


Zombies

If you have already downloaded and run the game, you will see zombies running towards the main character. In this tutorial, we will describe how these zombies are created and how they choose what to do. It is fairly straightforward – but leaves a lot of room for more complex behaviour, if you wish to adapt it.

Zombie behaviour

  • Stand in a random position
  • If the player comes within a certain radius, run towards him
  • Keep moving towards the player in a straight line, unless the player goes out of range
  • If there is an obstacle in the way, stop
  • If we have reached the player, kill him

If you have followed the tutorial all the way through, you may remember that the player character originally moved in straight lines, just walking towards the clicked point. This is the behaviour that I have implemented for the zombies, with an additional bit of logic to check that the zombie is not about to walk into an obstacle (in our case, water).

Zombie.java

First off, when we create the zombie we stick him in a random start position using the same helper method as we do for the player – however, we make sure the zombie does not spawn close to the player. This is to stop zombies attacking the player as soon as the game launches – it is better to let the player make some movement first.

while(pos.dst(screen.gameController.getPlayer().getPos()) < attackRadius) {
            pos = screen.getWorld().getRandomStartPosition();
        }

As with the player character, the core of the class is in its update method:

public boolean update(Vector2 playerPos) {
    if (hasBeenShot) {
        return false;
    }

    if (withinAttackDistance(playerPos)) {
        updateOrientation(playerPos);
        moveTowardsPlayer(playerPos);
    }
    return touchingPlayer(playerPos);
}

Firstly, we check if the zombie has already been shot – if so, do nothing.

If the zombie is close enough to the player to attack (I have hard-coded the radius – the larger you make it, the harder the game is), it will rotate to look at him, and move towards him.

For an added creep-factor, you can make the zombies update orientation regardless of whether or not the player is in view – it adds a certain level of creepiness to know that they are all pointed at the player, all the time.

private void updateOrientation(Vector2 playerPos) {
    float angle = (float) Math.toDegrees(Math.atan2(playerPos.x - pos.x, playerPos.y - pos.y));
    this.orientation = - angle;
}

The updateOrientation logic should look familiar to you if you have followed the tutorial – it is very similar to the way it works for the player character.

private void moveTowardsPlayer(Vector2 playerPos) {
    diff = playerPos.cpy();
    diff.sub(pos);

    diff.setLength(ZOMBIE_SPEED);

    Vector2 nextPos = pos.cpy().add(diff);

    // attempt to stop zombies crossing water
    Pair nextPair = screen.getWorld().getPair((int)nextPos.x, (int)nextPos.y);
    if (nextPair.isLand) {
        pos.set(pos).add(diff);
    }

}

Again, for moving towards the player we use vector maths to calculate the next step. We only update the zombie’s position if he is moving to a land tile – if not, he gets stuck. This does make the zombie pretty stupid – you can try changing this if you want.

The ZOMBIE_SPEED is hard-coded, and if you want a more challenging game, try increasing it – it definitely makes the game more fun.

private boolean touchingPlayer(Vector2 playerPos) {
    return pos.dst(playerPos) < 0.5;
}

The last step is to check if the zombie is touching the player, and return that value – if it is, we wanted to take some action. In this version, we kill the player (and the zombie). In future, we might allow (for example) three zombies to hit the player before he dies.

Creating the zombies

In GameController, during the creation of a new game, we allocate our zombies.

private void createZombies() {
   zombies = new ArrayList<Zombie>();
   for(int i=0; i < startingZombies; i++) {
      zombies.add(new Zombie(screen));
      zombiesAlive++;
   }
}

Fiddle with the startingZombies variable if you want to try the game out with more or fewer zombies.

No path-finding?

Instead of having the zombies run in a straight line, you can implement Jump Point Search for them. However, just in the interests of running quickly on a mobile phone, I have decided to make my zombies do no path-finding.

Conclusion

We have seen how to create some simple zombies and imbue them with basic behaviour. Next up, we will learn how to shoot them.



If you like these tutorials, or want to see a game that was written in much the same way, please download Dinowar from the Google Play Store. It costs nothing and is ad-free.

Leave a Reply