Tilemap Colliders

In this tutorial I will show how to setup tilemap colliders, and how to adjust the collider shape for your sprites.
To start this tutorial of, we will have to do some minor code change to TileGrid.cs

We currently use Texture2D type to be assigned in our groundtiles and objecttiles
and we make a new sprite and assign this texture to it, but this is actually unnecessary and
causes problems later on. To fix this, I changed the type Texture2D to Sprite and renamed the variable from ‘Texture’ to ‘Sprite’. As shown below:

[Serializable]
class GroundTiles
{
	public GroundTileType TileType;
	public Sprite Sprite;
	public Color Color;
	public Tile Tile;
}

[Serializable]
class ObjectTiles
{
	public ObjectTileType TileType;
	public Sprite Sprite;
	public Color Color;
	public Tile Tile;
}

As an affect of this, you must also change the CreateTile method in TileGrid.cs

private Tile CreateTile(Color color, Sprite sprite)
{
	// No sprite specified, we create one for the color instead
	bool setColor = false;
	Texture2D texture = sprite == null ? null : sprite.texture;
	if (texture == null)
	{
		setColor = true;
		// Created sprites do not support custom physics shape
		texture = new Texture2D(TileSize, TileSize)
		{
			filterMode = FilterMode.Point
		};
		sprite = Sprite.Create(texture, new Rect(0, 0, TileSize, TileSize), new Vector2(0.5f, 0.5f), TileSize);
	}

	// Create an instance of type Tile (inherits from TileBase)
	var tile = ScriptableObject.CreateInstance<Tile>();

	if (setColor)
	{
		// Make sure color is not transparant
		color.a = 1;
		// Set the tile color
		tile.color = color;
	}

	tile.sprite = sprite;

	return tile;
}

We now pass along the Sprite instead of the texture, incase we pass null (no value for sprite) then it will still use color, but no collider will be generated for colors.
One more minor change is in the method call, to replace tiletype.Texture with tiletype.Sprite in the InitializeTiles method also in Tilegrid.cs
Additionally we also added tile.colliderType setting to Sprite, this will allow
custom collider shape for our sprites (this is explained below).

private Dictionary<int, Tile> InitializeTiles()
{
	var dictionary = new Dictionary<int, Tile>();

	foreach (var tiletype in GroundTileTypes)
	{
		if (tiletype.TileType == 0) continue;

		// If we have a custom tile, use it otherwise create a new tile
		var tile = tiletype.Tile == null ?
			CreateTile(tiletype.Color, tiletype.Sprite) :
			tiletype.Tile;
        tile.colliderType = Tile.ColliderType.Sprite;

		dictionary.Add((int)tiletype.TileType, tile);
	}

	foreach (var tiletype in ObjectTileTypes)
	{
		if (tiletype.TileType == 0) continue;

		// If we have a custom tile, use it otherwise create a new tile
		var tile = tiletype.Tile == null ?
			CreateTile(tiletype.Color, tiletype.Sprite) :
			tiletype.Tile;
        tile.colliderType = Tile.ColliderType.Sprite;

		dictionary.Add((int)tiletype.TileType, tile);
	}

	return dictionary;
}

A side-effect of this change is that the assigned textures are now invalid, as they expect Sprite:

You can simply redrag your Oak tree texture into the Sprite slot to fix this.

Now that we have the code changes out of the way, we can start doing the collider adjustments to our tilemap object in Unity. To first explain how I want to approach this.
I want colliders for all of my “non-walkable” objects. This means objects such as rocks, trees, maybe even water (i’ll come back to this one later). But not things such as flowers, sticks (finer nature details that can be walked over).

To achieve this, we will have to either create another tilemap layer to differentiate these 2 layers, or you can specify the ColliderType on the Tile Asset. There are 3 types:

None: No collider will be generated
Grid: The collider will fit the grid cell shape
Sprite: The collider will fit the sprite’s Custom Physics Shape

With our previous changes, it is now always default on Sprite, but we will add an extension later to change it.

For now I will go with the last option, this means I will allow colliders on both groundtypes and objecttypes layer to show also how to make colliders for the water.
Start by adding a Tilemap Collider 2D component to your ObjectMap gameobject and run the game after and check the scene view for your trees.

As you can see it generates a basic collider shape on your objects layer, however in some cases the shape might not be exactly a perfect fit or how you would like it to be.
To fix this we can go to the texture -> sprite editor -> custom physics shape
And edit the shape here, this shape defines what the collider will be.

The “Generate Physics Shape” checkbox will actually automatically let unity generate a physics shape based on the texture, but it will not always be accurate. I believe this is enabled by default. I usually disable this for sprites which have a custom physics shape created by myself.
Select Custom Physics Shape

Now if you don’t see a default basic shape, press the generate button and adjust the shape how you like. Here is mine:

Now press apply in the top right corner, and if you now start your game scene again,
In the scene view you will see that the collider has changed.


Collider performance

The collision checking/detection process could be made more performant by using a Composite Collider 2D and allow the Tilemap Collider 2D to use this Composite collider.

What this does exactly: The Composite Collider 2D uses the vertices (geometry) from any of the colliders, and merges them together into new geometry controlled by the Composite Collider 2D itself. This means you will have less vertices for colliders that touch eachother, meaning less workload while doing collider checks + it can prevent ghost collisions.

Pros:
– More efficient collision detection
– Prevents ghost collisions (getting stuck between tiles that visually line up correctly)

Cons:
– Updating tiles after the composition is defined makes the collider regenerate the whole composition. (causes framelag)

You should use the composite collider only in any of the following cases:
1). You are not updating tiles of the tilemap the collider is on during gameplay.
2). You can offload this tilemap collider recompositioning process asynchronously and the user does not directly need the correct collider composition within the same frame.
(eg, updating tiles not within the players view range)

Composite collider configuration:

Adding this collider, also adds a rigidbody automatically, we wanna make sure the body type is set to Static (A Static Rigidbody 2D is designed to not move under simulation at all; if anything collides with it, a Static Rigidbody 2D behaves like an immovable object (as though it has infinite mass). It is also the least resource-intensive body type to use. A Static body only collides with Dynamic Rigidbody 2Ds).
Used By Composite flag must be enabled, in newer unity version this property is called “Composite Operation” and it should be set to “Merge”.
Changing extrusion factor and offset distance to 0 can also increase compositioning process speed.

If you run the game again you can now see in the scene view, the colliders are now merged
on sides where it touches each other, the collider is also a bit darker on the Static body type.

You can see the colliders merge where they touched, and now you only see 4 colliders on the picture instead of 8

Now to adjust our generation a little bit, to make it easier and definable if we want a collider for a certain object or not, we can add a selection for the ColliderType on our tiles.
Then this will generate the collider based on the type, and its the easiest way to move forward.

To prevent anymore code duplication we can add a base class for our GroundTiles and ObjectTiles like so:

[Serializable]
class GroundTiles : TileData
{
	public GroundTileType TileType;
}

[Serializable]
class ObjectTiles : TileData
{
	public ObjectTileType TileType;
}

class TileData
{
	public Sprite Sprite;
	public Color Color;
	public Tile Tile;
	public Tile.ColliderType ColliderType;
}

And we added the ColliderType to the base here as well.
Now we adjust the InitializeTiles method to take our colliderType:

private Dictionary<int, Tile> InitializeTiles()
{
	var dictionary = new Dictionary<int, Tile>();

	foreach (var tiletype in GroundTileTypes)
	{
		if (tiletype.TileType == 0) continue;

		// If we have a custom tile, use it otherwise create a new tile
		var tile = tiletype.Tile == null ?
			CreateTile(tiletype.Color, tiletype.Sprite) :
			tiletype.Tile;
        tile.colliderType = tiletype.ColliderType;

		dictionary.Add((int)tiletype.TileType, tile);
	}

	foreach (var tiletype in ObjectTileTypes)
	{
		if (tiletype.TileType == 0) continue;

		// If we have a custom tile, use it otherwise create a new tile
		var tile = tiletype.Tile == null ?
			CreateTile(tiletype.Color, tiletype.Sprite) :
			tiletype.Tile;
        tile.colliderType = tiletype.ColliderType;

		dictionary.Add((int)tiletype.TileType, tile);
	}

	return dictionary;
}

We can now define it in the inspector in unity for each of our tiles

We use sprite here for the tree, because we want it to use the Custom Physics Shape

Now to do this for the water tile, we want to use “Grid” collider type, as the water fills a whole cell.

Now we can do the same steps to add a Tilemap Collider 2D and a Composite Collider 2D on our GroundMap gameobject, and don’t forget to set the rigidbody body to static and set the Used by composite checkbox on the Tilemap Collider 2D, in newer unity version this property is called “Composite Operation” and it should be set to “Merge”.

Basically the same settings as the ObjectMap collider

Now run the game, and check the sceneview. You can see a nice collider line around the water, however its quite dark, i’ve changed the body type in paused mode to dynamic so we can see the line more clearly as it is brighter

Alright this is it, an easy way to define basic colliders and custom shapes to your tilemaps.

The code is available on my github repository:
https://github.com/Venom0us/Code2DTutorials/blob/1cc3417075897bb9fd4ff66912797b0223a4d071/Assets/Tilemaps/TileGrid.cs

Source files:
https://github.com/Venom0us/Code2DTutorials/releases/tag/v9

Leave a comment

Design a site like this with WordPress.com
Get started