Not every game that uses tilemaps has the game’s coordinate systems correspond exactly to the tiles. We can write a wrapper class around our tilemap to convert between tiles and world coordinates.

We will start with the generic implementation of a tilemap from a recent post. Next to a basic `Tilemap<T>`

class we also created a `Tile<T>`

type which represents a reference to a specific tile in a tilemap.

We will use that type, as well as plain coordinates to interact with the class we will create.

The purpose of this class is to make it as easy as possible for us to work with our tilemap. We would like to get the tile for any game world coordinates, as well as get the position of any tile, and possibly more.

## Implementing spacial wrapper class

The basic implementation of our class will be relatively simple. It needs to know our tilemap, and we have tell it about the scale and offset to use for coordinate conversions.

The scale represents the size of each individual tile in game-world coordinates, and the offset determines where the tilemap is located with respect to the world’s origin.

In many games an offset of (0, 0) may be fine, but **it sometimes makes things easier to think of the game world centered around the origin**, extending equally in all directions. In that case we want to make sure the tilemap is centered around the origin as well.

Let us call the class `TilemapSpacialWrapper<T>`

where `T`

is the type of tile.

The class’ constructor could look something like this:

```
class TilemapSpacialWrapper<T>
{
private readonly Tilemap<T> tilemap;
private readonly Vector2 offset;
private readonly float tileSize;
private readonly float tileSizeInv;
private readonly float tileSizeHalf;
public TilemapSpacialWrapper(Tilemap<T> tilemap, Vector2 offset, float tileSize)
{
this.tilemap = tilemap;
this.offset = offset;
this.tileSize = tileSize;
this.tileSizeInv = 1f / tileSize;
this.tileSizeHalf = tileSize / 2f;
}
}
```

I am already pre-calculating the half and the inverse tile size here, since we will need them later, and pre-computing them will speed up our eventual coordinate conversions.

We could easily add further constructors that could do some of the work for us. For example, we might prefer specifying the size of the level as well as the size of the tiles, and have the level class create the tilemap for us.

Let us assume we mean also center the world and tilemap around the origin, in which case the constructor might look as follows.

```
public TilemapSpacialWrapper(float worldWidth, float worldHeight, float tileSize)
{
var tilesX = (int)Math.Ceil(worldWidth / tileSize);
var tilesY = (int)Math.Ceil(worldHeight / tileSize);
this.tilemap = new Tilemap<T>(tilesX, tilesY);
this.offset = new Vector2(tilesX, tilesY) * (-tileSize / 2);
this.tileSize = tileSize;
this.tileSizeInv = 1f / tileSize;
this.tileSizeHalf = tileSize / 2f;
}
```

## Converting between coordinate systems

With this data in place, we can now write methods to convert between the coordinate systems.

Here are some ideas for functionality I would like to expose:

- get the tile at a point in the world
- get the center position of a tile
- get the top left position of a tile

We can implement the first using indexers, and the other two using methods as follows.

```
public Tile<T> this[Vector2 point]
{
get { /* return tile at point */ }
}
public Vector2 GetTileCenter(Tile<T> tile)
{
/* get the center position of the tile */
}
public Vector2 GetTileTopLeft(Tile<T> tile)
{
/* get top left position of the tile */
}
```

To be able to implement these, we need to be able to convert between the two coordinate systems. Since we are dealing with two axis aligned euclidean spaces, we can use a very simple scaling for this.

```
private void tileSpaceToPosition(float tx, float ty, out float x, out float y)
{
x = tx * this.tileSize + this.offset.X;
y = ty * this.tileSize + this.offset.Y;
}
private void positionToTileSpace(float x, float y, out float tx, out float ty)
{
tx = (x - this.offset.X) * this.tileSizeInv;
ty = (y - this.offset.Y) * this.tileSizeInv;
}
```

Using these two methods, we can create one more useful helper function that will call `positionToTileSpace`

and convert the returned coordinates into proper tilemap coordinates (i.e. cast floating points coordinates to integers).

```
private void positionToTile(float x, float y, out int tx, out int ty)
{
this.tileSpaceToPosition(x, y, out x, out y)
tx = (int)x;
ty = (int)y;
}
```

With these helpers in place, we can implement our indexer and two methods from above.

```
public Tile<T> this[Vector2 point]
{
get
{
int x, y;
this.positionToTile(point.X, point.Y, out x, out y);
return new Tile<T>(this.tilemap, x, y);
}
}
public Vector2 GetTileCenter(Tile<T> tile)
{
return this.GetTileTopLeft(tile)
+ new Vector2(this.tileSizeHalf, this.tileSizeHalf);
}
public Vector2 GetTileTopLeft(Tile<T> tile)
{
float x, y;
tileSpaceToPosition(tile.X, tile.Y, out x, out y);
return new Vector2(x, y);
}
```

These are the basic but essential operations this wrapper class helps us perform.

## Conclusion

Today we have looked at how to convert between single direct coordinates and tile coordinates when working with a tilemap.

We can expand the implemented class further to include not only single-tile queries, but range queries that return all tiles in a certain area, like a rectangle, or circle, or all tiles intersecting a ray.

But those are topics for future posts.

Enjoy the pixels!

## 4 comments

Hey Paul, got your other part to work! Thank you for the help. I have this one all working as well except for one thing… It doesn’t seem to like the

return this.GetTileTopLeft(tile)

+ new Vector2(this.tileSizeHalf, this.tileSizeHalf);

or

this.offset = new Vector2(tilesX, tilesY) * (-tileSize / 2);

Getting this error: Operator ‘*’ cannot be applied to operands of type ‘Vector2’ and ‘float’

Perhaps its my Vector2 Class? The class I made for Vector2 is pretty simple:

class Vector2

{

`public float X;`

public float Y;

public Vector2(float X, float Y)

{

this.X = X;

this.Y = Y;

}

`}`

Hey Scott,

Sorry for the late reply. To make the multiplication between vector and float work out you need to define your own operators like explained here: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/overloadable-operators

What I do though is use the maths types from OpenTK, which can do that and a lot more.

For example, the Vector2 struct is here: https://github.com/opentk/opentk/blob/cd0e300785865626465a1280f1820f29e45bdee0/src/OpenTK/Math/Vector2.cs

Hope that helps,

Paul

Hi Paul,

I’ve really enjoyed your lessons. Thank you for doing all of this. I managed to get this all setup, but I was wondering if you could provide an example of this being used? I am still having trouble wrapping my head around where I need to start in order to start assigning tiles to specific coordinates.

Thanks,

Jack

Hey Jack,

I don’t actually have any examples of where I’m using this code specifically.

The best I can give you right now is this small side project I’m working on: https://github.com/beardgame/td

We use very similar code there, the main difference being that our tilemap is hexagonal – it’s made from hexagons, not squares. That’s a big code base though, so not sure how helpful that is.

Overall, it really depends on what you’re doing. If you’re a bit lost, I recommend you start small. Maybe you can find another tutorial for tilemap based games that is a bit more complete than my posts.

Regards,

Paul