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.

Moving a character around the map 4 – Camera, Viewport


Code for this tutorial is on this git branch:

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

Have a look here for help setting it up


Viewport

A viewport is Libgdx’s way of managing your camera’s width and height. Although we will not be resizing our viewport during the game, it means that you do not need to worry about different sizes of screens.

We are using a specific Libgdx viewport – the StretchViewport.

import com.badlogic.gdx.utils.viewport.StretchViewport;

From the documentation:

A ScalingViewport that uses Scaling.stretch so it does not keep the aspect ratio, the world is scaled to take the whole screen

When you run the game on your computer, you can resize the window and see how everything updates to the correct size. This is the viewport managing the camera/graphics for you.

large small

You can see from these two (by the size of the title text) that one is zoomed and one is not, but the viewport has dealt with the resizing fine.

ZombieGame extends MyGame

When we launch the game, the ZombieGame class creates our viewport:

@Override
protected void initViewport() {
   // Total view width
   float viewWidth =32;
   float viewHeight = Math.round(Gdx.graphics.getHeight()/(Gdx.graphics.getWidth()/viewWidth));

   // World width, in squares. Always 100 wide.
   worldWidth = 32 * 3;
   worldHeight = (int) Math.floor(viewHeight);
   
   setViewport(new StretchViewport(viewWidth,viewHeight));
}

initViewport() is an abstract method, so you have no choice but to implement it.

Our first two lines are to set the width and height of our viewport.

float viewWidth =32;
float viewHeight = Math.round(Gdx.graphics.getHeight()/(Gdx.graphics.getWidth()/viewWidth));

The extra logic there is to maintain the ratio we want.

The next two lines are to set the size of the actual world that the player walks around in.

worldWidth = 32 * 3;
worldHeight = (int) Math.floor(viewHeight);

That *3 means that the world is going to be three times larger than the size of the viewport. If you want to prove that to yourself – set the worldWidth to just be 32, and you will see that it is the same size as the viewport.

Try fiddling with these variables and see the effects it creates. The wider your view, the further out you are zoomed.

Finally, all that’s left to do is create and set the viewport so the game knows about it.

setViewport(new StretchViewport(viewWidth,viewHeight));

What about the camera?

The StretchViewport creates its own instance of a camera – an OrthographicCamera.

An OrthographicCamera is one which projects 3D images into 2D. This image from Wikipedia should hopefully show you what is meant by that:

By Yuri Raysper - Has drawn from the bookThis vector image was created with Inkscape., Public Domain, https://commons.wikimedia.org/w/index.php?curid=1484146
By Yuri Raysper – Has drawn from the book This vector image was created with Inkscape., Public Domain, https://commons.wikimedia.org/w/index.php?curid=1484146

So it is good for 2D games – in the case of 3D games (outside the scope of this tutorial series), it would not be used.

All we want this camera to do is follow the player, to always be looking down on him. Therefore, we need a reference to this camera and then to update it every time the player moves.

PlayerInputSystem

When we create our PlayerInputSystem, we create a reference to the camera:

public PlayerInputSystem(MainScreen screen) {
		this.screen = screen;
		camera = (OrthographicCamera) ZombieGame.getViewport().getCamera();
	}

Now, we can update it when we want.

Why do we keep our reference in the PlayerInputSystem? This is so we can directly access the camera when pressing buttons. For example:

@Override
public boolean keyDown(int keycode) {
   if (keycode == Keys.R) {
      screen.gameController.createMap();
      screen.gameController.setRunning(true);
   }
   else if (keycode == Keys.Q) {
      screen.gameController.endGame();
   }
   
   float moveAmount = 1.0f;
   
   if (keycode == Keys.UP) {
      camera.position.y += moveAmount;
   } else if (keycode == Keys.DOWN) {
      camera.position.y -= moveAmount;
   } else if (keycode == Keys.LEFT) {
      camera.position.x -= moveAmount;
   } else if (keycode == Keys.RIGHT) {
      camera.position.x += moveAmount;
   } else if (keycode == Keys.Z) {
      camera.zoom += moveAmount;
   } else if (keycode == Keys.X) {
      camera.zoom -= moveAmount;
   }
   
   updateCamera();
   
   return false;
}

You may recognise this as code we were playing with in the first round of tutorials. This is why we have the camera here – however, you may choose to move it. It slightly irritates me having the camera here, as the PlayerInputSystem class should deal with input and nothing else. It may move in a future tutorial.


Aside: You may have also noticed that this code doesn’t work any more. I can’t zoom! I can’t navigate left and right! Is it broken? Actually, every time the game updates now, it centres the camera above the player. So this code runs, but you never see the effects of it. To get it working again, comment out everything in the updateCamera() method. Though this will, of course, stop the camera from centering over the player.


UpdateCamera() is where the action happens:

public void updateCamera() {
   if (camera == null || camera.position == null || getPlayer() == null) {
      return;
   }

   camera.position.x = getPlayer().getX();
   camera.position.y = getPlayer().getY();

}

We have a bit of code safety first, just to make sure we aren’t going to null-pointer before we’ve set up all our objects.

Then we simply set the camera’s location to be that of the player. We don’t need to worry about translating this into the correct on-screen co-ordinate, because the viewport deals with that for us.

Finally

That is pretty much it for now! The final thing I wanted to highlight was the viewport’s project/unproject functionality, which makes converting from screen co-ordinates to map co-ordinates very easy. I already wrote about this in the previous tutorial – go back to it if you missed it – but I wanted to highlight that this is another positive for the viewport.



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.

Moving a character around the map 2 – Movement and Orientation


Code for this tutorial is on this git branch:

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

Have a look here for help setting it up


If you have got the project up and running, you should be looking at a character moving around the screen when you click.

ZombieGamePlayerOnScreen

If you can not get the project to run, post a message at the bottom and we’ll sort it out.

PlayerCharacter

The PlayerCharacter is where we have our logic which deals with orientation and movement. We also draw our sprite here – it draws itself to a SpriteBatch when requested.

Movement and orientation is all done with vector logic, using Libgdx’s built-in Vector2 class.

When we set up the PlayerCharacter, we give him a random start position. The code to pick a random spot is quite straightforward for now – anywhere on the map which is classed as land.

Every time the PlayerCharacter’s update() method is called, the player will see whether a destination has been set, see if we are already at the destination, and if not – move towards it and point at it.

Movement

When we click in the PlayerInputSystem, we pass a tile to the PlayerCharacter to set as the destination. We also do this:

diff = destination.asVector().cpy();
diff.sub(pos);

diff is of type Vector2. diff is now a Vector2 representing the vector between the destination and the player’s current position.

Note: we’ve added a method to our Pair class, to convert it to a Vector2.

Next, we update(). First we check that we actually have a destination (if you haven’t clicked yet, it will be null!). Then we check that we aren’t already standing at the destination (or close to it). If either of these things are the case, we do nothing.

Assuming we want to update our position, we do this:

diff.setLength(PLAYER_SPEED);
pos.add(diff);

diff is still a vector pointing in the same direction as before, but with a fixed length (whatever you set PLAYER_SPEED as – try playing with it to see the effect). Our position is also a vector, so we add this ‘diff’ vector to our current position, and it gives us a new vector which is an addition of the two.

Vector addition
A visual representation of adding some vectors. Think of A, B and C as the results of 3 different clicks

Orientation

Ok, we have updated our position. Now for the orientation. All we do is work out the angle between two vectors (our position and destination):

private void updateOrientation() {
   float angle = (float) Math.toDegrees(Math.atan2(destination.x - pos.x, destination.y - pos.y));
   this.orientation = - angle + 90f;
}

Why did we add that +90f at the end? It is just to correct for the player image looking the wrong way. If you just updated the actual image to be pointing another 90 degrees round, you would get the same effect. I probably could have done that in the time in took me to write this explanation…

That’s it! You’ve updated the orientation and position variables. Now, when we are told to draw the player, we have all the information necessary.

public void draw(GameController game, SpriteBatch batch) {
   playerSprite.setCenter(pos.x, pos.y);
   playerSprite.setOrigin(1, 1);
   playerSprite.setRotation(orientation);
   playerSprite.draw(batch);
}

We’re walking on water!

Yes we are. This is the most basic implementation of movement I could put in for now; we don’t even check whether we can proceed without touching water. Once we’ve moved on to later tutorials, we’ll actually be doing path-finding each time we touch somewhere. We will abandon this approach – naively move in a straight line – and the player will walk around bodies of water!

If you fancy a challenge now, and to improve your understanding of how things tie together, why don’t you stop the player from being able to walk on water? It doesn’t require any path-finding.

We’re going to use path-finding instead. So is this logic useless to us?

No! A feature will we add in a few tutorials is to fire bullets at zombies. This logic will be used for that – sprites moving in a straight direction, able to move over land or water – perfect for bullets.

Conclusion

We’ve taken a look at vector logic for moving in straight directions, and orienting sprites to ‘look at’ what we want them to look at.

Next up:

  • Determining which tile we have just touched
  • Having a camera track the player


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.

Moving a character around the map


Code for this tutorial is on this git branch:

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

Have a look here for help setting it up


Up until now we have generated a random map, and got a camera pointing at it. Now, we move to something more interesting – moving a character around a map.

ZombieGamePlayerOnScreen2

This tutorial is the second time we will download a pre-existing project that I have added to git. It’s the same project that you checked out earlier (assuming you’ve followed along this tutorial), but a different branch.

With this code, you will be able to do the following:

  • Place a character on our map
  • Click somewhere else on the map and have the character travel towards where you clicked
  • Track the character’s movement with the camera

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

Check out the branch with the command above. We’re not going to go over every code change, but we’ll look at the core stuff.

The flow of logic

  • GameController creates a player character
  • PlayerInputSystem detects a TouchDown event (a click on a computer, a touch on a touchscreen). Determine what coordinate on the screen was touched. Tells the player to treat this coordinate as its destination
  • GameController update() method is called, in turn calls the update() method of PlayerCharacter
  • PlayerCharacter compares its current position, its destination, and updates its orientation and position
  • GameScreenRenderer draws the map (as it was doing before) and draws the player
  • Repeat!

Getting graphics

man

I’ve provided an image to use for our character, but I didn’t create it. I took it from here:

http://opengameart.org/content/animated-top-down-survivor-player

So all credit for the image goes to ‘rileygombart’, who has made his/her graphics available. These kind of websites are a great source of graphics if you are artistically challenged (as we are – graphics for Dinowar were done by a friend).

I’ve added this graphic to the project and called it ‘man’.

Remember the tutorial on Texture Packing? Well, we will need to rebuild out atlases to include this. Then later we just ask for ‘man’ from the TextureAtlas. If you’re running the project as it is, this should happen automatically when you run the code.

ZombieGamePlayerOnScreen

Hopefully you are seeing something like this when you run the code! If not, do get in touch.

Try clicking around to see how the player sprite behaves. Next up, we’ll go into more detail about the new code.

 



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.

TextureAtlas and TexturePacker


Code for this tutorial is on this git branch:

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

Have a look here for help setting it up


So far in this tutorial, we have a drawn a map. We’ve gone in to detail about how we’ve created the data structure that is the map layout, but we haven’t spoken much about the mechanics of how we store and display the actual graphics. This might seem a bit straightforward for the map (it is just a single tile drawn repeatedly), but when we start introducing moving characters (next tutorial after this one!) it will be more important.

early_map
Quite clear from this image – the same square tile drawn repeatedly to make up the map
feature_graphic
The tiles aren’t quite so obvious here, but each one is a different type of tile – for example, a corner tile, a side tile. Each dinosaur is also a sprite which makes use of a texture from an atlas

If you are at all familiar with 2-D game programming, you’ll probably be familiar with the concept of sprites and that each one has an image (or texture) that is what you see represents the sprite.

What is a TextureAtlas?

Think of it as one large image which you load into memory. This image is all your other images stuck together, side-by-side, in one large ‘texture’ (or a subset of them). Then, there is an accompanying json file (formatted text) which tells a program where each individual image is. So it will say e.g. top-left for 50px width, 50px height is an image called ‘zombie’. Then when I ask for the texture called ‘zombie’, I’ll get back just the relevant part of the overall image.

Why use a TextureAtlas rather than a bunch of individual images?

Check out the wikipedia article for a good description:

https://en.wikipedia.org/wiki/Texture_atlas

but basically – faster memory usage. As well as this, the atlas makes it very easy to deal with your graphics. Drop an image in a folder, re-build your atlas, and then ask for it by name.

Here’s a bit of code from Dinowar:

String spriteName = "Dinosaur__"+ fromBase.getOwner().getPlayerColor().name() +" Air";
Sprite sprite = gameController.getMainScreen().getMainAtlas().createSprite(spriteName);

We are creating a sprite directly from the atlas. createSprite(String name) is a built-in Libgdx method.

Building the TextureAtlas with the TexturePacker

How to create our atlas though? It would not be a time-saver if you had to create the json file and giant image yourself. This is where the TexturePacker comes in. We referred earlier to some unused code in the DesktopLauncher:

if (packTextures) {
   TexturePacker.process(settings, assets_general + "images", assets_android + "atlas", "main");
   settings.duplicatePadding = true;
   TexturePacker.process(settings, assets_general + "tiles", assets_android + "atlas", "tiles");
}

We’re building two atlases here. We’re specifying where the files we want to pack are with the second parameter:

assets_general + "images"

We’re specifying where to save the atlas with the third parameter:

assets_android + "atlas"

And we’re naming our atlas with the fourth parameter:

"main"

At the moment, this code section is not running because we have set packTextures to false. Why don’t you try changing it to true and running it?

Check the console, and you should see output like this:

images
Packing………
Writing 512×1024: atlas/main.png
tiles
Packing………
Writing 1024×512: atlas/tiles.png
127 74

Let’s check out file locations. For me on a Mac, these are:

Original files – /Users/dave/git/zombiegametutorial/assets_general/images

Created Atlas – /Users/dave/git/zombiegametutorial/android/assets

And the folder we’ve written out to now contains these files:

atlas files

Let’s take a look inside the tiles.atlas:tiles.atlas

It refers to the image name, some metadata about the image, and then lays out the different texture areas – in our case, there is just one, called “white”.

We’ve packaged our graphics up for use in our Android game – change it to iOS if you’re creating your iOS version of the game.

Note: we usually keep packTextures to false because we don’t need to create these atlases every time – just when we’ve made changes.

Creating the Atlas object in game

In MainScreen.java, we’ve got this method, called from the constructor:

private void setUpGraphics() {
// Set up graphics here for passing between classes
// Try to keep everything here to make for easy disposal at the end.
tilesAtlas = new TextureAtlas("atlas/tiles.atlas");
Tile.setSprites(tilesAtlas);
}

So we’re already set up with our atlas! Have a look in Tile.java to see what is going on.

public static void setSprites(TextureAtlas tiles) {
   plainTile = tiles.createSprite("white");
   
   // Make sure we do this, it then does it for the whole atlas.
   plainTile.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
}

We create our sprite, and later when the draw() method is called, we’ll draw it on to the screen. This occurs when our GameScreenRenderer has its update() method called, and it in turn tells each Tile to draw itself.

Conclusion

We’ve seen how to pack a collection of images into an atlas, and how to then reference that atlas to draw sprites on the screen.

These tile sprites aren’t very interesting though – they’re just a white square! In our next tutorial, we’ll get a character moving around the screen, and so we’ll be using an atlas for this – you’ll find though, that the principles are exactly the same as the tiles that we have drawn here.



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.