Using a tmx map in monogame

From TRCCompSci - AQA Computer Science
Revision as of 13:25, 8 February 2024 by Admin (talk | contribs) (Other TiledSharp options)
Jump to: navigation, search

Install TiledSharp

The easiest method is to create your MonoGame project then:

  1. Click on project & select Nuget Package Manager
  2. Search online for TiledSharp & Install
  3. In the Game1.cs of your project add:
    using TiledSharp;
    

Simple Setup

Declarations

Add the following declaration within the main class in Game1.cs:

        TmxMap map;
        Texture2D tileset;

        int tileWidth;
        int tileHeight;
        int tilesetTilesWide;
        int tilesetTilesHigh;

Load Content

Download the files here. You will need to copy these into the content folder of your project, you will also need to add the png file to the content pipeline and build it. Without the xnb file an error will be thrown.

In the LoadContent method add the following to load your map & tiles, it also sets the size of the tiles:

            map = new TmxMap("Content/exampleMap.tmx");
            tileset = Content.Load<Texture2D>(map.Tilesets[0].Name.ToString());

            tileWidth = map.Tilesets[0].TileWidth;
            tileHeight = map.Tilesets[0].TileHeight;

            tilesetTilesWide = tileset.Width / tileWidth;
            tilesetTilesHigh = tileset.Height / tileHeight;

Draw the Map

In the draw method, add the following code to draw each tile of the map:

        spriteBatch.Begin();
        foreach(var layer in map.Layers) // loop to cycle through each layer
        {  
            for (int i = 0; i < layer.Tiles.Count; i++) // loop to cycle through each tile
            {
                int gid = layer.Tiles[i].Gid;

                // Empty tile, do nothing
                if (gid == 0)
                {

                }
                else
                {
                    int tileFrame = gid - 1;
                    int column = tileFrame % tilesetTilesWide;
                    int row = (int)Math.Floor((double)tileFrame / (double)tilesetTilesWide);

                    float x = (i % map.Width) * map.TileWidth;
                    float y = (float)Math.Floor(i / (double)map.Width) * map.TileHeight;

                    Rectangle tilesetRec = new Rectangle(tileWidth * column, tileHeight * row, tileWidth, tileHeight);

                    spriteBatch.Draw(tileset, new Rectangle((int)x, (int)y, tileWidth, tileHeight), tilesetRec, Color.White);
                }
            }
        }
        spriteBatch.End();

Moving the map

We are going to setup the player controls to get the map movement required, we will record the movement and then adjust the spriteBatch.Draw() command in the Draw method.

So firstly create a new rectangle called bounds, add it to the main declarations at the top of the program:

Rectangle bounds;

In the initialise method set this to be (0,0,0,0):

bounds = new Rectangle(0, 0, 0, 0);

If your map has an object to show the starting point you could set bounds in the LoadContent method as well.

In the Update method add this code to get the keyboard input from the user:

KeyboardState keyState = Keyboard.GetState();

int scrollx = 0, scrolly = 0;

if (keyState.IsKeyDown(Keys.Left))
   scrollx = -10;
if (keyState.IsKeyDown(Keys.Right))
   scrollx = 10;
if (keyState.IsKeyDown(Keys.Up))
   scrolly = 10;
if (keyState.IsKeyDown(Keys.Down))
   scrolly = -10;
            
bounds.X = bounds.X + scrollx;
bounds.Y = bounds.Y + scrolly;

For each of the arrow keys this will increase or decrease the value of X or Y. You could introduce a speed variable, for example speed=10 and leave the changes in the if statements to be either 1 or -1. your program could then vary speed accordingly.

Now in the draw method change the code to this:

Rectangle newView = new Rectangle((int)x - bounds.X, (int)y - bounds.Y, tileWidth, tileHeight);
spriteBatch.Draw(tileset, newView, tilesetRec, Color.White);

Firstly it replaces the code

new Rectangle((int)x, (int)y, tileWidth, tileHeight)

from the spriteBatch.Draw() command with a rectangle we create called newView.

newView is the same as the code we replaced except we add the bounds.X to the x first, and the bounds.Y to y first. Now any movement of the arrow keys will change the X & Y in bounds, which should then change the view of the map.

Other TiledSharp options

You can load layers of your map individually:

var collision = map.Layers[2];

This can be used to load in a collision layer from your map, you can then use this to test for collisions between the player and the tiles in the layer.

            bool check = false;
            for (int i = 0; i < collision.Tiles.Count; i++) // loop to cycle through each tile
            {
                int gid = collision.Tiles[i].Gid;

                // Empty tile, do nothing
                if (gid == 0)
                {

                }
                else
                {
                    float x = ((i % map.Width)+1) * map.TileWidth;
                    float y = ((float)Math.Floor(i / (double)map.Width)+1) * map.TileHeight;
                    Rectangle tilerec = new Rectangle((int)x,(int)y,map.TileWidth, map.TileHeight);
                    if (tilerec.Intersects(bounds))
                    {
                        check = true;
                        break;
                    }
                }
                if(bounds.X<= bounds.Width || bounds.X>=map.Width*map.TileWidth)
                {
                    check = true;
                }
                if (bounds.Y <= bounds.Height || bounds.Y >= map.Height * map.TileHeight)
                {
                    check = true;
                }
            }

You can also access the object groups and objects within your map:

var hiddenChest = map.ObjectGroups["Chests"].Objects["hiddenChest"];

You could set an object for the start or end point, spawn locations etc.