LibGDX Tutorial 11: Tiled Maps Part 1: Simple Orthogonal Maps

16. April 2014

 

If you’ve never used Tiled read this first!

 

In this tutorial part we are going to look at loading orthogonal maps in LibGDX.  Orthogonal basically means “at a right angle to” which is a fancy way of saying the camera is looking straight at the scene.  Another much less fancy word for this style of game is “top down”.  Common examples include almost every single game from the 16Bit generation of consoles such as Zelda or MegaMan.

 

One of the first problems you are going to encounter is how do you create your maps?  One very common solution is the Tiled Map Editor which fortunately I just completed a tutorial on!  This tutorial assumes you know how to generate a TMX file, so if you haven’t be sure to go through the linked tutorial.  Fortunately LibGDX makes it very easy to work with TMX files.

 

First things first we need to copy the TMX and any/all tilemap image files used to your assets folder, like so:

image

You may notice unlike earlier tutorials I am currently using IntelliJ.  With the recent switch to Gradle it is much easier to get up and running in IntelliJ and I massively prefer it to Eclipse.  That said, everything I say is equally valid in Eclipse or IntelliJ and when there are differences, I will point them out.  If you want to get started with IntelliJ read here.

 

Alright, back on topic…

 

Now that you have the map and tilesets in your project, let’s jump right in with the code:

 

package com.gamefromscratch;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapRenderer;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;

public class TiledTest extends ApplicationAdapter implements InputProcessor {
    Texture img;
    TiledMap tiledMap;
    OrthographicCamera camera;
    TiledMapRenderer tiledMapRenderer;
    
    @Override
    public void create () {
        float w = Gdx.graphics.getWidth();
        float h = Gdx.graphics.getHeight();

        camera = new OrthographicCamera();
        camera.setToOrtho(false,w,h);
        camera.update();
        tiledMap = new TmxMapLoader().load("MyCrappyMap.tmx");
        tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap);
        Gdx.input.setInputProcessor(this);
    }

    @Override
    public void render () {
        Gdx.gl.glClearColor(1, 0, 0, 1);
        Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        camera.update();
        tiledMapRenderer.setView(camera);
        tiledMapRenderer.render();
    }

    @Override
    public boolean keyDown(int keycode) {
        return false;
    }

    @Override
    public boolean keyUp(int keycode) {
        if(keycode == Input.Keys.LEFT)
            camera.translate(-32,0);
        if(keycode == Input.Keys.RIGHT)
            camera.translate(32,0);
        if(keycode == Input.Keys.UP)
            camera.translate(0,-32);
        if(keycode == Input.Keys.DOWN)
            camera.translate(0,32);
        if(keycode == Input.Keys.NUM_1)
            tiledMap.getLayers().get(0).setVisible(!tiledMap.getLayers().get(0).isVisible());
        if(keycode == Input.Keys.NUM_2)
            tiledMap.getLayers().get(1).setVisible(!tiledMap.getLayers().get(1).isVisible());
        return false;
    }

    @Override
    public boolean keyTyped(char character) {

        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        return false;
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean scrolled(int amount) {
        return false;
    }
}

 

When you run the code you should see your map.  Pressing the arrow keys will scroll around the map ( and show bright red when you’ve moved beyond the extents of your map ) .  Pressing 0 or 1 will toggle the visibility of each of the two layers in your map.  ( See the tutorial on Tiled if that makes no sense ).

 

image

 

The impressive thing here is how little code was required to accomplish so much.  In a nutshell it was basically just this:

 

camera = new OrthographicCamera();
camera.setToOrtho(false,w,h);
camera.update();
tiledMap = new TmxMapLoader().load("MyCrappyMap.tmx");
tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap);

 

We create an OrthographicCamera, set it to the dimensions of the screen and update() it.  Next we load our map using TmxMapLoader.load() and create a OrthogonalTiledMapRenderer passing in our tiled map.

 

In our render method:

camera.update();
tiledMapRenderer.setView(camera);
tiledMapRenderer.render();

Basically update the camera ( in case we moved it using arrow keys ), pass it in to the TiledMapRenderer with setView() and finally render() the map.  That’s it.

As you can see in our key handler:

 

@Override
public boolean keyUp(int keycode) {
    if(keycode == Input.Keys.LEFT)
        camera.translate(-32,0);
    if(keycode == Input.Keys.RIGHT)
        camera.translate(32,0);
    if(keycode == Input.Keys.UP)
        camera.translate(0,-32);
    if(keycode == Input.Keys.DOWN)
        camera.translate(0,32);
    if(keycode == Input.Keys.NUM_1)
        tiledMap.getLayers().get(0).setVisible(!tiledMap.getLayers().get(0).isVisible());
    if(keycode == Input.Keys.NUM_2)
        tiledMap.getLayers().get(1).setVisible(!tiledMap.getLayers().get(1).isVisible());
    return false;
}

 

Navigating around the map is simply a matter of moving around the camera.  We move in 32pixel chunks because those are the size of our tiles.  So basically we move the camera left/right/up/down by one tile each time an arrow key is pressed.  In the event the 0 or 1 key are pressed we toggle the visibility of that particular layer.  As you can see, you can access the TileMap layers using the getLayers().get() function.

 

In the next part we will look at adding some more complex functionality.

 

On to part two.

Programming , , ,







blog comments powered by Disqus
Creating a game sprite: Texture mapping Part 2: Creating a UV Map
Home > Art

Creating a game sprite: Texture mapping Part 2: Creating a UV Map

6. September 2013

 

As I mentioned in the prior tutorial section creating a UVMap is basically like wielding a pair of virtual scissors to cut your 3D object up so it can be smushed flat.  As we saw earlier, the default unwrap map is pretty, subpar:

 

Default unwrap:

default

 

Let’s start cutting things up.  First we will default the wings.  In 3D view, make sure you are in Edit mode and Edge selection, then select the edges at the base of the wing, like so:

image

 

Now we want to mark the selected as a seam.  Press CTRL + E to bring up the Edge menu, and select Mark Seam.

image

 

Or you could use the button in the Tools panel of 3D View:

image

 

Both options do exactly the same thing.  Once the seem is marked it will appear as red:

image

 

Now you can see the result of marking a seam.  In 3D view, select all edges ( A ) and take a look at the UV window:

image

 

We now have a completely separate collection of UVs ( called a UV island ).  I still wouldn’t want to paint over these UVs yet, so we have a bit more work to do.  In order to be able to flatten the wing even more, lets mark another set of seams, like below:

image

 

Now look what happened to the UVs

image

 

That is certainly much easier to paint over!  Of course, you could have split the wing top and bottom easy enough but each additional seam makes painting a bit trickier.

 

Remember the textured view of our jet from the previous tutorial:

badUVs

Instead of a checkerboard texture on the wing, you just got a white blotch.  Now that we marked the wing seams, look at the result:

image

 

MUCH better!  Ultimately our goal is to make the entire jet have a checkered texture like that.  The process is exactly the same, just keep marking seems in the model until the UV layout makes sense.  Let’s repeat the same process on the back wing and then the tail.  If you make a mistake, you can simply Clear Seam on the selected seam.  To completely undo your changes, select all edges and do CTRL+E –> Clear Seam and you are back to default.

 

I marked the following seams on the back tail and wing:

image

 

 

Resulting in this UV layout:

image

 

And in our rendered view:

image

 

Next lets cut the jet in half vertically.  Select the continuous loop all the way around the jet, it will meet up with the wing cut:

Front:

image

 

Back:

image

 

Highlighted:

image

 

You want a continous edge around the entire jet that will cut it between top and bottom.  Once selected, mark the seam.

image

 

Now mark the area surrounding the air intake:

image

 

This however results in an unwanted seam, shown below marked in green:

image

 

Select those edges and then select Clear Seam.

 

Now we have:

image

 

And the following UV layout:

image

 

Keep in mind, Blender automatically sets the positions, so yours might be a bit different layout wise.

 

Finally, lets seperate the cockpit away from the fusalage:

image

 

The cockpit:

image

 

And finally, the tail:

image

 

And now our fully UV mapped model:

image

As you can see, the texture pattern flows pretty well over our image.  There are portions where we might get into a bit of trouble but they aren't areas where we are going to need details, so we should be good to go!

 

 

Well, that sucked, there has to be an easier way!

 

Yeah, actually, there is.  Blender has something called a Smart UV Project.  To use it, in 3D View, in edit mode, select all, then press U.  In the resulting menu select Smart UV Project:

 

image

 

Then a dialog will appear:

image

 

As I understand it, Smart UV looks at your model and separates UVs based on the angle between shapes.  So for example, since the wing essentially meets the fusalage at 90 degrees, it will easily be split into it’s own UV group.  Play around with the angle limit until you get a grouping you like.  Here is the result at 66 degrees.

 

image

 

Not ideal, but certainly a hell of a lot easier!

 

 

We only touched on a very small bit of what you can do with UVmaps.  Just keep in mind, if you add another seam ( and thus generate another Unwrap ), the positions will be reset.  Otherwise you can scale, turn, relocated, etc… UV coordinates to your hearts content.  If you don't want them to be scaled again, you can Pin (p) a UV in place.

 

Selection Options

 

You may find that you want to select Polygons from the UV layout screen.  By default it’s setup that what you select in the 3D view will be selected in the UV view, but it’s possible to do the opposite.

You need to click the “Keep UV and edit mode mesh selection in sync” button.

image

 

Now making a selection ( such as using the B)ox select mode ) in the UV window:

image

 

Will select the corresponding polygons in the 3D view:

image


Click here for the Next Part

 

Art ,







blog comments powered by Disqus