Difference between revisions of "Using a tmx map in monogame"
(→Draw the Map) |
(→Using Map Objects) |
||
(13 intermediate revisions by the same user not shown) | |||
Line 5: | Line 5: | ||
# In the Game1.cs of your project add: <syntaxhighlight lang=csharp>using TiledSharp;</syntaxhighlight> | # In the Game1.cs of your project add: <syntaxhighlight lang=csharp>using TiledSharp;</syntaxhighlight> | ||
− | =Declarations= | + | =Simple Setup= |
+ | ==Declarations== | ||
Add the following declaration within the main class in Game1.cs: | Add the following declaration within the main class in Game1.cs: | ||
<syntaxhighlight lang=csharp> | <syntaxhighlight lang=csharp> | ||
Line 17: | Line 18: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | =Load Content= | + | ==Load Content== |
Download the files [https://drive.google.com/file/d/0Bw-0YEA_JX9gUHdXUWNiMWxGeXc/view?usp=sharing 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. | Download the files [https://drive.google.com/file/d/0Bw-0YEA_JX9gUHdXUWNiMWxGeXc/view?usp=sharing 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. | ||
Line 33: | Line 34: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | =Draw the Map= | + | ==Draw the Map== |
In the draw method, add the following code to draw each tile of the map: | In the draw method, add the following code to draw each tile of the map: | ||
<syntaxhighlight lang=csharp> | <syntaxhighlight lang=csharp> | ||
− | + | spriteBatch.Begin(); | |
− | foreach(var layer in map.Layers) | + | foreach(var layer in map.Layers) // loop to cycle through each layer |
{ | { | ||
− | for (int i = 0; i < layer.Tiles.Count; i++) | + | for (int i = 0; i < layer.Tiles.Count; i++) // loop to cycle through each tile |
{ | { | ||
int gid = layer.Tiles[i].Gid; | int gid = layer.Tiles[i].Gid; | ||
Line 59: | Line 60: | ||
Rectangle tilesetRec = new Rectangle(tileWidth * column, tileHeight * row, tileWidth, 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.Draw(tileset, new Rectangle((int)x-(screen.Width / 2), (int)y-(screen.Height / 2), tileWidth, tileHeight), tilesetRec, Color.White); |
} | } | ||
} | } | ||
Line 66: | Line 67: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | =Moving the map= | + | ==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. | 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. | ||
Line 103: | Line 104: | ||
Now in the draw method change the code to this: | Now in the draw method change the code to this: | ||
<syntaxhighlight lang=csharp> | <syntaxhighlight lang=csharp> | ||
− | Rectangle newView = new Rectangle((int)x | + | Rectangle newView = new Rectangle((int)x - bounds.X, (int)y - bounds.Y, tileWidth, tileHeight); |
spriteBatch.Draw(tileset, newView, tilesetRec, Color.White); | spriteBatch.Draw(tileset, newView, tilesetRec, Color.White); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 111: | Line 112: | ||
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. | 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. | ||
− | = | + | =Map Collisions= |
You can load layers of your map individually: | You can load layers of your map individually: | ||
<syntaxhighlight lang=csharp> | <syntaxhighlight lang=csharp> | ||
− | var | + | var collision = map.Layers[2]; |
</syntaxhighlight> | </syntaxhighlight> | ||
+ | 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. | ||
+ | |||
+ | <syntaxhighlight lang=csharp> | ||
+ | bool check = false; | ||
+ | for (int i = 0; i < collision.Tiles.Count; i++) // loop to cycle through each tile | ||
+ | { | ||
+ | int gid = collision.Tiles[i].Gid; // get the id of the tile used | ||
+ | |||
+ | // ignore empty tiles | ||
+ | if (gid != 0) | ||
+ | { | ||
+ | float x = ((i % map.Width)+1) * map.TileWidth; // calculate the x coordinate | ||
+ | float y = ((float)Math.Floor(i / (double)map.Width)+1) * map.TileHeight; // calculate the y coordinate | ||
+ | |||
+ | Rectangle tilerec = new Rectangle((int)x,(int)y,map.TileWidth, map.TileHeight); // create a rectangle for the tile | ||
+ | if (tilerec.Intersects(bounds)) | ||
+ | { | ||
+ | check = true; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | =Using Map Objects= | ||
You can also access the object groups and objects within your map: | You can also access the object groups and objects within your map: | ||
<syntaxhighlight lang=csharp> | <syntaxhighlight lang=csharp> | ||
var hiddenChest = map.ObjectGroups["Chests"].Objects["hiddenChest"]; | var hiddenChest = map.ObjectGroups["Chests"].Objects["hiddenChest"]; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
You could set an object for the start or end point, spawn locations etc. | You could set an object for the start or end point, spawn locations etc. | ||
+ | |||
+ | ===Annoyingly=== | ||
+ | The creator of TiledSharp has declared all of the set accessors to be private. This means you will not be able to interact with the map object directly (i.e. move the player object or hide objects collected). You can get around this by downloading the source code from GitHub and make which ever set accessors public. If you do this, just be aware the NuGet package as map.Layers[] the code from GitHub uses map.TileLayers[]. | ||
+ | |||
+ | ==Drawing Objects== | ||
+ | <syntaxhighlight lang=csharp> | ||
+ | foreach(TmxObject to in map.ObjectGroups["Objects"].Objects) | ||
+ | { | ||
+ | if(to.Visible) | ||
+ | _spriteBatch.Draw(pixel, new Rectangle((int)to.X - bounds.X + (screen.Width / 2), (int)to.Y - bounds.Y + (screen.Height / 2), (int)to.Width, (int)to.Height), Color.White); | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | I have integrated this into the main drawing code of the map, within the foreach loop which cycles through the layers: | ||
+ | |||
+ | <syntaxhighlight lang=csharp> | ||
+ | if(layer==collision) | ||
+ | { | ||
+ | foreach(TmxObject to in map.ObjectGroups["Objects"].Objects) | ||
+ | { | ||
+ | if(to.Visible) | ||
+ | _spriteBatch.Draw(pixel, new Rectangle((int)to.X - bounds.X + (screen.Width / 2), (int)to.Y - bounds.Y + (screen.Height / 2), (int)to.Width, (int)to.Height), Color.White); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> |
Latest revision as of 09:26, 13 February 2024
Contents
Install TiledSharp
The easiest method is to create your MonoGame project then:
- Click on project & select Nuget Package Manager
- Search online for TiledSharp & Install
- 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-(screen.Width / 2), (int)y-(screen.Height / 2), 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.
Map Collisions
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; // get the id of the tile used
// ignore empty tiles
if (gid != 0)
{
float x = ((i % map.Width)+1) * map.TileWidth; // calculate the x coordinate
float y = ((float)Math.Floor(i / (double)map.Width)+1) * map.TileHeight; // calculate the y coordinate
Rectangle tilerec = new Rectangle((int)x,(int)y,map.TileWidth, map.TileHeight); // create a rectangle for the tile
if (tilerec.Intersects(bounds))
{
check = true;
break;
}
}
}
Using Map Objects
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.
Annoyingly
The creator of TiledSharp has declared all of the set accessors to be private. This means you will not be able to interact with the map object directly (i.e. move the player object or hide objects collected). You can get around this by downloading the source code from GitHub and make which ever set accessors public. If you do this, just be aware the NuGet package as map.Layers[] the code from GitHub uses map.TileLayers[].
Drawing Objects
foreach(TmxObject to in map.ObjectGroups["Objects"].Objects)
{
if(to.Visible)
_spriteBatch.Draw(pixel, new Rectangle((int)to.X - bounds.X + (screen.Width / 2), (int)to.Y - bounds.Y + (screen.Height / 2), (int)to.Width, (int)to.Height), Color.White);
}
I have integrated this into the main drawing code of the map, within the foreach loop which cycles through the layers:
if(layer==collision)
{
foreach(TmxObject to in map.ObjectGroups["Objects"].Objects)
{
if(to.Visible)
_spriteBatch.Draw(pixel, new Rectangle((int)to.X - bounds.X + (screen.Width / 2), (int)to.Y - bounds.Y + (screen.Height / 2), (int)to.Width, (int)to.Height), Color.White);
}
}