Prettier Maps


Code for this tutorial is on this git branch:

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

Have a look here for help setting it up


This is a screenshot from Dinowar, our Android game written with Libgdx. The random map generation is very similar to what we have implemented in this Zombie tutorial.

Compare that to what we have done so far in this tutorial series:

Until now, we haven’t put a lot of effort into making the maps look better. In this tutorial, we will make some changes so that our maps look more like this:

50% water

Notice how the edges are much cleaner, they are have curved corners and nicer edges. There is also a clear, coloured border line.

20% water

Let’s take a look at how we have achieved this. Firstly, we have created a bunch of new images (which you can use in your own projects). These are under the assets_general/tiles folder.

These images might not look obviously related to the screenshots above – but we will see how they form to create the rounded edges that we want.

Terrain transitions

At the moment, the borders of the land are just solid, blocky divisions and look very rough. To make our map look good, we need some way of making the transitions between these different types of tile look more natural.

The solution we have used is taken from this tutorial:

https://www.gamedev.net/resources/_/technical/game-programming/tilemap-based-game-techniques-handling-terrai-r934

I strongly recommend you go read this tutorial – it is not long, and I do not see the point of reproducing the content here.

But, to summarise as quickly as possible – for each tile, we work out if it touches a tile of a different type (in our case, where land and water touch) and work out what bits of the tile we should draw. But seriously, go read that tutorial.

When water meets land, the water ‘overlaps’ the ground to allow for a smoother looking transition.

Our implementation

We calculate the the transition information for each tile when we create the map. We loop through all the tiles and work out which transition images we want to apply to each one, then store that information in the Tile object. Each tile has been assigned a number of sprites which sit on top of one another to achieve the full effect. Then each time we draw, we draw every sprite for each individual tile.

Check out the changes in the following classes:

Tile.java, TileSprite.java, Pair.java and MapTools.java

Tile.java

Firstly, we load in all the sprites that we are going to use in setSprites().

Now, when a Tile is created, we create our corners and edges that will be drawn on each update.

public Tile (Pair pair) {
   makeCorners(pair);
   makeEdges(pair);
}

I’m not going to reproduce all the code here – check out the project and look for yourself. But the makeCorners() and makeEdges() methods set up what the tile is going to look like based on its neighbours (i.e. its surrounding tiles).

TileSprite.java

Now, when we draw our tiles (and TileSprites), we draw the ‘solid’ and ‘line’ sections’ of each tile. The ‘line’ parts are the borders – you can see what I mean with the following screenshot – this has had the lines removed:

Compare that to a previous screenshot. Personally I think it looks much better with the lines – but if you don’t want them, delete the following in TileSprite.java:

if (linePart!=null) {
   linePart.setBounds(pair.x, pair.y, 1, 1);
   linePart.setColor(lineColor);
   linePart.draw(batch);
}

Then delete code around adding the lines – but I don’t think you’ll want to do this.

Conclusion

I haven’t gone into a huge amount of detail about the code in this tutorial, but it is not that complicated – the important thing is to understand the concept, and your best way to do that is to read the tutorial I linked to above (and here) on GameDev.net. The code provided is based on the ideas from the GameDev tutorial.

It transforms a blocky map into a much nicer, smoother looking map. And it is possible for more than just land and water – if you are creating a more detailed map, you can fiddle with the order of precedence of your map details to get them to smoothly transition into one another.



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.

Firing bullets at 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


Firing bullets

Also included in the latest code is the ability to fire bullets at zombies. They way the player actually does this is to touch on or close to the zombie.

In the earliest iteration of this, I had the zombies simply die when clicked on. This was very unsatisfying, so I implemented proper bullets. In the current version of the game, I don’t think it’s actually possible to miss – the zombies run directly at you, so when you fire a bullet in their direction, you are guaranteed to hit. However, you could imagine a development of the game where the zombies run from side-to-side, and so bullets could easily miss.

The bullets continue on when they hit a zombie, so they can actually hit multiple zombies on the map if they happen to be aligned.

Aiming

As described, the mechanism for firing is to click on/touch a zombie. In our PlayerInputSystem, we introduce a method for checking if the place we just touched is on a zombie.

private Zombie getTouchedZombie(int x, int y) {
   Vector2 touch = new Vector2(x,y);
   for (Zombie z : screen.getGameController().getZombies()) {
      if (!z.hasBeenShot() && z.getPos().dst(touch) < 2) {
         return z;
      }
   }

   return null;
}

All we do is loop over our zombies and check if they are close to the touch. This is some pretty basic ‘collision detection’. We call this method every time we touch somewhere on the screen. If it returns a zombie, we fire at it.

Zombie shot = getTouchedZombie(x, y);

if (shot != null) {
   getPlayer().shootZombie(shot);
   return false;
}

Note we also return from the touchDown method – we don’t want to walk towards the zombie. Whenever we shoot, the player stops walking.

Bullets

The Bullet class is where all of the logic for an individual bullet resides. It is created and then the fire() method is called, and given its starting point and its destination (i.e. the location of the zombie we have fired at).

public void fire(Vector2 destination, PlayerCharacter character) {
    currentPosition = character.getPos().cpy();
    endPosition = destination.cpy();

    dir = endPosition.cpy();
    dir.sub(currentPosition);
    dir.setLength(BULLET_SPEED);
}

Let’s look at what is going on here. In the first two lines, we assign our currentPosition to be the same as the character’s, and the endPosition to be the vector we have passed in.

We set the dir (direction) variable to be the difference between the start and end points, but at a shorter length. You can vary this length depending on how fast you want your bullets to move.

When we update then, we just keep adding that dir variable to the currentPosition to keep our bullet moving in a constant direction at constant speed:

public void update(float delta) {
    currentPosition.set(currentPosition).add(dir);
    sprite.setPosition(currentPosition.x, currentPosition.y);
}

Bullets colliding with zombies

Finally, we need some collision detection between zombies and bullets. This will work in the same way as the other basic collision detection we use (when checking whether a player has clicked near a zombie, or if a zombie is touching the player). Every time we update the zombies, we check whether they have been hit by a bullet.

This is in the GameController:

// check if a zombie has been hit by a bullet
for(Bullet b : bullets) {
   if (b.isLive() && z.getPos().dst(b.getPos()) < 1.0f) {
      z.shoot();
   }
}

If the bullet is still live, and it is very close to the zombie, we consider that a collision and kill the zombie by triggering its shoot method. (We also keep track of the number of shot zombies, but I’ve removed that from here just for clarity).

At this point of the development of the game, the zombie’s shoot method simply tells it that it has been shot. This means that when zombies come to be updated later, this zombie will not have its update method triggered, and the player can no longer interact with it, for example by dying when walking over it. We also set the zombie’s sprite to be a satisfyingly gory blood splatter.

Conclusion

In this tutorial we have shown how to fire bullets and have them ‘hit’ zombies.

There are still some improvements to be made for this, which I may work on in future (though it’s not a priority). These include:

  • collision detection could probably be more efficient
  • at time of writing, bullets never ‘die’. This would be fairly easy to implement though – just keep count of how far a bullet has travelled and set  ‘live = false’ after a certain distance
  • the bullet does not come out of the player character’s gun, but just starts from his ‘position’ on the map. Again, this could be done fairly easily I think – but it seemed fiddly so I haven’t bothered (particularly as I might change the player sprite in future)

Next up will either be some kind of GUI, or perhaps map improvements – the map still looks a bit crap at the moment. In Dinowar we have logic to work out corners and how edges fit together, something that I may implement in a future tutorial for this.

Dinowar edges made smooth


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.