Importing Tiles

Importing and rendering the map

After making our maps, drawing them onto tile layers and then marking and identifying stuff on the object layers, we can now move on to making sure your game loads the map onto our Game screen.

There are built-in tools and classes that help us load the map, namely the TmxMapLoader, TiledMap, and the TiledMapRenderer. Since I made our tile map’s orientation to be orthogonal, I’ll be using the OrthogonalTiledMapRenderer.  If you used the isometric or hexagonal map orientation, then use those map renderers instead. 

private TmxMapLoader mapLoader; //helps load the map
private TiledMap map; //the loaded map object
private OrthogonalTiledMapRenderer renderer; //renders the map

public LevelOne(DemoGame game)
{
    //existing constructor code
    mapLoader = new TmxMapLoader(); //create an instance of built-in map loader object 
    map = mapLoader.load("levelOne.tmx"); //using map loader object, load the tiled map that you made
    renderer = new OrthogonalTiledMapRenderer(map); //render the map.  
}

Notice how our mapLoader.load() method takes in a String of the filename. Since filenames can be changes easily and might be required elsewhere, it is recommended that you create a Constants class and include that filename as a final String instead. So that line of code can become something like this:

map = mapLoader.load(Constants.MAP_FILENAME); //using map loader object, load the tiled map that you made

After creating the variables, we have to ensure that our map is actually rendered onto our game screen. We can do this by using our renderer object in the render() method like so: (Make sure to also remove any Textures you’ve drawn in the batch from the previous video about creating screens!)

@Override
public void render(float delta)
{
    renderer.render();
}

As good practice, we should also always dispose of resources that we no longer need to use at the end of the game screen’s life cycle:

@Override
public void dispose()
{
    map.dispose();
    renderer.dispose();
}

Cameras and Viewports

Now since our map is very big and we would like to only show our player what they should be seeing, for example, follow Mario around, we would be using something called a Camera, which honestly works pretty similar to a real-life video camera.  Another benefit to using a Camera is that if the application window is changed, we can define how our screen changes according to the application window.  This is done by defining the Viewport.  A viewport is similar to the display monitor that shows what the camera will capture when taking a photo, and it can decide how to show the display to the player, either through stretching the original photo, or to add black bars to the sides of the photo.  

There’s 2 types of cameras, OrthographicCamera and PerspectiveCamera.  The main difference is that the perspective camera emulates the function of the human eye, and therefore the further away something is, the smaller it seems.  To put it simple, if you’re making a 2D game, you’ll most likely want to use the Orthographic camera.  If you’re making a 3D game, you’ll want to use the perspective camera.  In this video, since I’m making a simple 2D game, I’ll be using the Orthographic camera.  

private OrthographicCamera camera;

public LevelOne(DemoGame game)
{
//existing constructor code
camera = new OrthographicCamera();
}

Now moving onto Viewports.  There are a few existing options, or you can always extend the Viewport class and make your own.  The existing options are:

  • ExtendViewport: always maintains aspect ratio by extending the world in one direction and having it scale to fit in the other direction.
  • FillViewport: always maintain the aspect ratio and fill the viewport accordingly, even if it means having to crop out parts of the image.
  • FitViewport: Always maintains aspect ratio and shows the whole image, fills the other parts with black bars (or whatever you want it to fill with)
  • StretchViewport: Stretches image according to the viewport’s size
  • ScreenViewport: Where the viewport would be the same as your computer screen’s resolution.

It’s probably still quite confusing and hard to have a good grasp of what the differences between each viewport is, so if you would want to know more, there’s a short section with visual comparisons to how each of them differ in the video lesson. A good thing to consider when deciding which Viewport to use is how adjusting the application window’s size will affect the contents displayed.

In our game, we want to use the FitViewport, so the size of the world does not get changed or distorted even though the application window is resized. We also need to reposition our camera so it shows the middle of our map rather than the bottom left corner. Notice how I used a Constants class again for the Width and Height of the Viewport.

private Viewport viewport;

public LevelOne(DemoGame game)
{
//existing constructor code
viewport = new FitViewport(Constants.WIDTH, Constants.HEIGHT, camera); //camera variable from before
camera.position.set(viewport.getWorldWidth() / 2, viewport.getWorldHeight() / 2, 0); //set initial camera position

}

@Override
public void resize(int width, int height)
{
    viewport.update(width, height); //viewport gets adjusted accordingly
}

We also want our player to only see what our camera can see, and in order to do that we need to ensure our batch and our map renderer knows what the camera can see. I created a private method update() here (which will mainly be used for later) and added those lines of code inside. It is also recommended that you clear the screen during each render iteration so avoid any overlapping artifacts from previous and outdated renders.

 private void update(float dt)
{
    camera.update();
    renderer.setView(camera); //sets the view from our camera so it would render only what our camera can see.
    }

@Override
public void render(float delta)
{
    update(delta);
    //clear screen
    Gdx.gl.glClearColor(0, 0 , 0 ,1);
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

    //existing code

    myGame.getBatch().setProjectionMatrix(camera.combined); //updates our batch with our Camera's view and projection matrices.
}

In order to check if the whole map was loaded correctly, you can add a temporary method to pan move the camera towards the right to view the whole map.

//TEMP METHOD TO SHOW ENTIRE MAP
private void handleInput(float dt)
{
    if(Gdx.input.isKeyJustPressed(Input.Keys.RIGHT)) //if up button pressed)
    {
        camera.position.x += 5 * 16; //move 5 tiles per right press
    }
}

@Override
public void render(float delta)
{
    update(delta);
    handleInput(delta);
    //existing code
}

Video Lesson

https://drive.google.com/file/d/1l5BRsDHmBqhAOq3wkTGySNsNcCfx7dTj/view