Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
22. June 2014

 

Title kinda says it all, LibGDX 1.2 was released today.  The update includes:

 

 

I don’t think the breaking changes will impact any of our tutorial series.  Interesting to see the new AI extension, will be nice to see how that develops ( A* next? ).  Improved IntelliJ build times are certainly welcome.  You can get started here.

News


4. May 2014

 

You know one of the things that suck about working in Java?

 

Java.

 

Or more precisely the Java runtime.  The need to have the Java runtime pre-installed in order to run your game is a bit of a pain.  Fortunately Badlogicgames, the people behind LibGDX have released Packr.

 

In their own words:

Packages your JAR, assets and a JVM for distribution on Windows (ZIP), Linux (ZIP) and Mac OS X (.app), adding a native executable file to make it appear like the app is a native app. Packr is most suitable for GUI applications.

 

To be honest, that explanation kinda sucks, their description on Reddit is much better.

This has been a pain point for a few of our users, though Minecraft made pretty much anyone on this planet install Java anyways :)

I wrote a little tool called packr which does the following:

  • bundle your jar/assets with an embedded JRE
  • add a native executable for the respective platform to make your app look native
  • minimize the JRE (zipped: 23mb, extracted 40mb)

This should make the user experience a bit better, as there aren't any things the user needs to have installed before trying your game/app. All they need to do is download a ZIP/App Bundle and execute the native executable.

It's very easy to use, either via CLI arguments, a JSON config file, or by invoking it directly from code (it's really a library).

 

There are a couple limitations right now:

    • Icons aren't set yet on any platform, need to do that manually.
    • Windows is 32-bit only, Linux is 64-bit only, Mac OS X is 64-bit only
    • JRE minimization is very conservative, depending on your app, you can carve out stuff from a JRE yourself, disable minimization and pass your custom JRE to packr

 

If this sounds useful to you, you can learn more at the Packr github page.

News


1. May 2014

 

In the prior tutorial we got a simple top down tile map working.  Now we want to add some character to it, literally.  Depending on your needs this can be laughably simple or somewhat complex.  Let’s take a look at a basic example.  This is building on our previous code example and if you’ve worked through the prior tutorials in this series, there is nothing new here yet.

 

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.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
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;
    SpriteBatch sb;
    Texture texture;
    Sprite sprite;
    
    @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);

        sb = new SpriteBatch();
        texture = new Texture(Gdx.files.internal("pik.png"));
        sprite = new Sprite(texture);
    }

    @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();
        sb.begin();
        sprite.draw(sb);
        sb.end();
    }

    @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;
    }
}

 

 

So what exactly did we do?  Well first off, we added this sprite to the project in the android/asset folder.

pik

It might look somewhat familiar… 

In create() we allocate a sprite batch, load our texture and create a sprite with it.  Finally in render() after the map is drawn, we simply draw our sprite to the batch.  Order is of critical importance.  One other thing I should probably point out… I don’t dispose() of anything, so this code leaks like mad…  Now if you run the code you see:

image

 

So far so good! 

 

First problem we’ve got here is, how do we position our sprite within the world?  In this case I am simply drawing at the origin, but how would we go about positioning the sprite in the tile a user clicks?  Let’s go ahead and see!

 

Add the following to the touchDown handler:

public boolean touchDown(int screenX, int screenY, int pointer, int button) {
    Vector3 clickCoordinates = new Vector3(screenX,screenY,0);
    Vector3 position = camera.unproject(clickCoordinates);
    sprite.setPosition(position.x, position.y);
    return true;
}

 

Now we need to make a small change to our sprite batch.  We need to apply our camera transformations to it, so when we scroll the screen around using the arrow keys, our sprite stays put.  This is easily accomplished in render():

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();
    sb.setProjectionMatrix(camera.combined);
    sb.begin();
    sprite.draw(sb);
    sb.end();
}

 

Now when you run it, the sprite will draw where you click in the world:

image

 

There’s a small possible problem here… what if you wanted your sprite to be BEHIND those rocks, or items in the higher level layers? 

 

There are a couple of ways to do this.  First you could actually create a layer for player sprites.  You could simply create a layer in Tiled that you kept empty and add your sprite into it.  Here instead I am going to do so programmatically.  Let me make something perfectly clear, this is most certainly *NOT* how you would do it, but it does give an interesting bit of insight into how tiles and layers are composed.

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);

    sb = new SpriteBatch();
    texture = new Texture(Gdx.files.internal("pik.png"));
    sprite = new Sprite(texture);

    
    // Get the width and height of our maps
    // Then halve it, as our sprites are 64x64 not 32x32 that our map is made of
    int mapWidth = tiledMap.getProperties().get("width",Integer.class)/2;
    int mapHeight = tiledMap.getProperties().get("height",Integer.class)/2;

    // Create a new map layer
    TiledMapTileLayer tileLayer = new TiledMapTileLayer(mapWidth,mapHeight,64,64);
    
    // Create a cell(tile) to add to the layer
    TiledMapTileLayer.Cell cell = new TiledMapTileLayer.Cell();
    
    // The sprite/tilesheet behind our new layer is a single image (our sprite)
    // Create a TextureRegion that is the entire size of our texture
    TextureRegion textureRegion = new TextureRegion(texture,64,64);
    
    // Now set the graphic for our cell to our newly created region
    cell.setTile(new StaticTiledMapTile(textureRegion));
    
    // Now set the cell at position 4,10 ( 8,20 in map coordinates ).  This is the position of a tree
    // Relative to 0,0 in our map which is the bottom left corner
    tileLayer.setCell(4,10,cell);

    // Ok, I admit, this part is a gross hack. 
    // Get the current top most layer from the map and store it
    MapLayer tempLayer = tiledMap.getLayers().get(tiledMap.getLayers().getCount()-1);
    // Now remove it
    tiledMap.getLayers().remove(tiledMap.getLayers().getCount()-1);
    // Now add our newly created layer
    tiledMap.getLayers().add(tileLayer);
    // Now add it back, now our new layer is not the top most one.
    tiledMap.getLayers().add(tempLayer);
}

 

And look, we are behind a tree!

image

 

 

The comments explain it all really.  We basically are programmatically creating a new Tile Layer within the map and positioning it below the top most layer.  You would never do it this way for a sprite for several reasons.  First you will only be able to move in cell by cell chunks ( aka, 64 pixels at a time ).  In certain maps that makes sense, like old school Ultima 4 type role playing games.  Next, to move you actually will have to remove the tile from it’s current location and add it to the new one.  Finally, it’s probably pretty slow to boot.  However if you want to dynamically populate a layer, say with power ups or things you create programmatically instead of using the level editor, this is how you can do it.

 

What about when dealing with the players sprite and moving it only a few pixels at a time?  Well that’s slightly more complicated.

 

One option you have, especially if you want to use the Sprite class, is to extend the map rendering class, in this case OrthogonalTiledMapRenderer then override the render method and render your sprite(s).  Here is a simple example:

package com.gamefromscratch;

import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;

import java.util.ArrayList;
import java.util.List;

public class OrthogonalTiledMapRendererWithSprites extends OrthogonalTiledMapRenderer {
    private Sprite sprite;
    private List<Sprite> sprites;
    private int drawSpritesAfterLayer = 1;

    public OrthogonalTiledMapRendererWithSprites(TiledMap map) {
        super(map);
        sprites = new ArrayList<Sprite>();
    }

    public void addSprite(Sprite sprite){
        sprites.add(sprite);
    }

    @Override
    public void render() {
        beginRender();
        int currentLayer = 0;
        for (MapLayer layer : map.getLayers()) {
            if (layer.isVisible()) {
                if (layer instanceof TiledMapTileLayer) {
                    renderTileLayer((TiledMapTileLayer)layer);
                    currentLayer++;
                    if(currentLayer == drawSpritesAfterLayer){
                        for(Sprite sprite : sprites)
                            sprite.draw(this.getSpriteBatch());
                    }
                } else {
                    for (MapObject object : layer.getObjects()) {
                        renderObject(object);
                    }
                }
            }
        }
        endRender();
    }
}

 

Now we simply use it in place of the other TileMapRenderer.  Not now that we no longer need a SpriteBatch, since we are using the map renderer’s.  Here’s the updated 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.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.math.Vector3;

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

        camera = new OrthographicCamera();
        camera.setToOrtho(false,w,h);
        camera.update();

        texture = new Texture(Gdx.files.internal("pik.png"));
        sprite = new Sprite(texture);

        tiledMap = new TmxMapLoader().load("MyCrappyMap.tmx");
        tiledMapRenderer = new OrthogonalTiledMapRendererWithSprites(tiledMap);
        tiledMapRenderer.addSprite(sprite);
        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) {
        Vector3 clickCoordinates = new Vector3(screenX,screenY,0);
        Vector3 position = camera.unproject(clickCoordinates);
        sprite.setPosition(position.x, position.y);
        return true;
    }

    @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;
    }
}

 

Now when you run it, where you click the player sprite will be positioned correctly and behind objects:

image

.

Under this system however, you just treat your sprite normally.  Update it’s position and it’s updated in the game world.

 

There is one other possible way to support this functionality, that is a bit of a hybrid of both approaches.  Like the earlier example, we add a new layer in to the map, in this case we are going to do it in Tiled instead of programmatically.

 

In Tiled, create a new object layer and position it like so:

image

 

Now we are going to access it using the MapObject class, specifically the TextureMapObject ( a somewhat confusingly named class… ).  Once again this requires a custom TileRenderer implementation, this time overriding the renderObject class, like so:

package com.gamefromscratch;

import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.objects.TextureMapObject;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;

public class OrthogonalTiledMapRendererWithSprites extends OrthogonalTiledMapRenderer {

    public OrthogonalTiledMapRendererWithSprites(TiledMap map) {
        super(map);
    }

    @Override
    public void renderObject(MapObject object) {
        if(object instanceof TextureMapObject) {
            TextureMapObject textureObj = (TextureMapObject) object;
                spriteBatch.draw(textureObj.getTextureRegion(), textureObj.getX(), textureObj.getY());
        }
    }
}

 

renderObject() is called to render the various well… MapObjects in the tilemap.  The default implementation is empty, so we need to provide one.  Here we simply check to see if our object is a TextureMapObject and if it is we draw it using the spriteBatch, much like before.

 

A side effect is we no longer use the Sprite class for positioning.  Instead we create a TextureRegion again and control position via the TextureMapObject setX/setY methods.  Let’s take a look at the result on our main application:

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.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.maps.MapLayer;
import com.badlogic.gdx.maps.objects.TextureMapObject;
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.math.Vector3;

public class TiledTest extends ApplicationAdapter implements InputProcessor {
    Texture img;
    TiledMap tiledMap;
    OrthographicCamera camera;
    TiledMapRenderer tiledMapRenderer;
    SpriteBatch sb;
    Texture texture;
    Sprite sprite;
    MapLayer objectLayer;

    TextureRegion textureRegion;

    @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 OrthogonalTiledMapRendererWithSprites(tiledMap);
        Gdx.input.setInputProcessor(this);
        texture = new Texture(Gdx.files.internal("pik.png"));

        objectLayer = tiledMap.getLayers().get("objects");
        textureRegion = new TextureRegion(texture,64,64);

        TextureMapObject tmo = new TextureMapObject(textureRegion);
        tmo.setX(0);
        tmo.setY(0);
        objectLayer.getObjects().add(tmo);
    }

    @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) {
        Vector3 clickCoordinates = new Vector3(screenX,screenY,0);
        Vector3 position = camera.unproject(clickCoordinates);
        TextureMapObject character = (TextureMapObject)tiledMap.getLayers().get("objects").getObjects().get(0);
        character.setX((float)position.x);
        character.setY((float)position.y);
        return true;
    }

    @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;
    }
}

 

It’s mostly the same, except of course the different renderer, the fact we no longer use a Sprite object and in touchDown() we locate the TextureMapObject in the “objects” layer and update its positions.

 

 

Which method you use is up to you, your personal preference and the requirements of your game.  Myself, I find the second to be probably the most clean.  Of course if your sprite doesn’t need to care about tile depth at all, you can ignore all of this and just use SpriteBatch over top and save yourself many headaches!

Programming


20. April 2014

 

So it’s taken a while, 4 years to be precise, but LibGDX, the cross platform Java game development engine, has finally hit the milestone release 1.0. 

 

Everyone thought the day would never come. But here it is. libGDX 1.0 is officially released! Let me quickly run you through the most important changes:

Read the full CHANGES file for more goodies.

To try out our new setup, start here!

If you have a Gradle-based project, make sure to update the gdxVersion to “1.0.0″ and refresh your IDE project files! The new snapshot (==nightly) version is “1.0.1-SNAPSHOT”

Besides the usual bug fixing and enhancements, we also cleaned up our libGDX repo, website and wiki for the 1.0 release. The old setup UI has been deprecated, the audio and image extensions have been removed, and the demos have been gradelized and put into their own repositories. You can now also directly test the demos in your browser (or desktop, or Android device)!

Finally, we’ve setup a Patreon, that allows users to contribute to our infrastructure costs. This has been so successful, that we were able to move our build and website server to Hetzner. After the move and adding some build magic, the build now takes 10 minutes instead of 1 hour and 45 minutes. Thanks to all the patrons, you really made a difference in my life!

Going forward, we’ll try to have a much shorter release cycle (2 weeks – 1 month). The major version of libGDX will stay at 1 for the foreseeable future. The minor version will be increased when API breaking changes are introduced. The patch version will be increased in case of bug fixes and API additions. Releasing often allows you to stay as up-to-date as possible before freezing your libGDX version for a release.

 

They also have a brief blurb about the future:

With all pieces in place, Q1 2014 was used to polish up libGDX’s user experience and documentation for the 1.0 release. We now support all JVM development environments (Eclipse, IDEA, Netbeans, CLI) through our Gradle-based builds. Our build server has been upgraded so we can push out new releases much more easily (and hence regularly!). Our repository has been cleaned up, any clutter has been removed. The Wiki has been updated to reflect the latest state of APIs and setup procedures. We are ready to pull the trigger. After 4 years of development, libGDX has finally reached version 1.0.

 

There is also a detailed history of the LibGDX project.  You can read all about it in the announcement.

 

Congratulations to the LibGDX team!

News


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


GFS On YouTube

See More Tutorials on DevGa.me!

Month List