Game Frameworks  
Lecture 06: 2D Tilemaps in Unity  
Edirlei Soares de Lima  
<edirlei.lima@universidadeeuropeia.pt>  
Tilemap  
•
The Tilemap component is a system which stores and handles  
Tile Assets for creating 2D levels.  
Tilemap Workflow  
•
General workflow to create and paint a Tilemap in Unity:  
1
. Create a Tilemap GameObject.  
• A Grid GameObject is also created automatically in the process.  
2
. Create Tile Assets or generate Tiles from Sprites, and then assign  
them to a Tile Palette.  
3
4
. Paint on a Tilemap with your Tile Palette.  
. Attach the Tilemap Collider 2D component to the Tilemaps to make  
them interact with Physics2D.  
Tilemap Workflow  
1
. Create a Tilemap GameObject.  
–
A Grid GameObject is also created automatically in the process.  
Tilemap Workflow  
2
. Create Tile Assets or generate Tiles from Sprites, and then assign them  
to a Tile Palette.  
–
Individual Tiles (Create -> Tile):  
–
Tiles from Sprites (Window -> 2D -> Tile Palette):  
https://edirlei.com/aulas/gameframeworks/tileset_example.png  
Tilemap Workflow  
3
. Paint on a Tilemap with your Tile Palette.  
Tilemap Workflow  
4
. Attach the Tilemap Collider 2D component to the Tilemaps to make  
them interact with Physics2D.  
Tilemap Layers  
•
Multiple Tilemaps can be added to the same grid, which  
allows the creation of different types of layers: background,  
foreground, collision, and event layers.  
–
Example: a layer dedicated to collision detection.  
Scripting – Accessing Tiles  
•
Example 1: Instantiate bombs on click that destroy tiles on collision.  
public class InstantiateBomb : MonoBehaviour  
{
public GameObject bombPrefab;  
public float cooldownTime = 0.5f;  
private float bombTimer = 0;  
void Update(){  
if ((Input.GetMouseButton(0)) && (bombTimer <= 0)){  
Vector2 screenPos = Input.mousePosition;  
Vector3 worldPos = GetComponent<Camera>().  
ScreenToWorldPoint(screenPos);  
Instantiate(bombPrefab, new Vector2(worldPos.x, worldPos.y),  
Quaternion.identity);  
bombTimer = cooldownTime;  
}
if (bombTimer >= 0){  
bombTimer -= Time.deltaTime;  
}
}
}
Scripting – Accessing Tiles  
•
Example 1: Instantiate bombs on click that destroy tiles on collision.  
public class Bomb : MonoBehaviour  
{
private void OnCollisionEnter2D(Collision2D collision)  
{
bool explode = false;  
Tilemap tilemap = collision.gameObject.GetComponent<Tilemap>();  
if (tilemap){  
Vector3 hitPosition = Vector3.zero;  
foreach (ContactPoint2D hit in collision.contacts){  
hitPosition.x = hit.point.x - 0.01f * hit.normal.x;  
hitPosition.y = hit.point.y - 0.01f * hit.normal.y;  
tilemap.SetTile(tilemap.WorldToCell(hitPosition), null);  
explode = true;  
}
}
if (explode){  
Destroy(gameObject);  
}
}
}
Scripting – Accessing Tiles  
•
Example 2: Create a new tile on collision (bomb smoke).  
public class Bomb : MonoBehaviour  
{
public Tile explosionTile;  
private Tilemap hitLayer;  
private void Start()  
{
hitLayer = GameObject.FindGameObjectWithTag("HitLayer").  
GetComponent<Tilemap>();  
}
private void OnCollisionEnter2D(Collision2D collision)  
{
bool explode = false;  
Tilemap tilemap = collision.gameObject.GetComponent<Tilemap>();  
if (tilemap)  
{
Vector3 hitPosition = Vector3.zero;  
...  
Scripting – Accessing Tiles  
•
Example 2: Create a new tile on collision (bomb smoke).  
...  
foreach (ContactPoint2D hit in collision.contacts)  
{
hitPosition.x = hit.point.x - 0.01f * hit.normal.x;  
hitPosition.y = hit.point.y - 0.01f * hit.normal.y;  
tilemap.SetTile(hitLayer.WorldToCell(hitPosition), null);  
hitLayer.SetTile(hitLayer.WorldToCell(hitPosition),  
explosionTile);  
explode = true;  
}
}
if (explode)  
{
Destroy(gameObject);  
}
}
}
Exercise 1  
1
) Create a small level for a platform game using tile maps and  
implement the following tasks:  
a) Implement the camera movement;  
b) Respawn the player at the beginning of the level when he falls off the  
platforms;  
c) Allow the player to drop bombs that explode and destroy the tiles  
around.  
•
The bomb must explode after 3 seconds.  
Hexagonal Tilemaps  
•
In addition to regular Tilemaps, Unity provides both  
Hexagonal Point Top and Hexagonal Flat Top Tilemaps.  
Hexagonal Flat Top  
Hexagonal Point Top  
Hexagonal Tilemaps  
•
Example: https://www.kenney.nl/assets/hexagon-pack  
Hexagonal Tilemaps  
•
Example: https://www.kenney.nl/assets/hexagon-pack  
Scripting – Hexagonal Character Movement  
•
Example 3: Character movement in a hexagonal tilemap (+ pathfinding)  
–
–
–
Character sprites: https://opengameart.org/content/zombie-sprites  
Import and slice sprite, and then create animation clips for idle and walk (all directions).  
Create a State Machine to control the animations:  
Scripting – Hexagonal Character Movement  
•
Example 3: Character movement in a hexagonal tilemap (+ pathfinding)  
–
Tileset: https://www.kenney.nl/assets/hexagon-pack  
–
Create the level: Tilemap and Collision Tilemap  
Scripting – Hexagonal Character Movement  
•
Example 3: Character movement in a hexagonal tilemap (+ pathfinding)  
public class Pathfinding{  
float Heuristic(Vector3Int w1, Vector3Int w2){  
...  
}
List<Vector3Int> ReconstructPath(Dictionary<Vector3Int, Vector3Int>  
cameFrom, Vector3Int current, Vector3Int start){  
...  
}
List<Vector3Int> GetNeighbors(Tilemap tilemap, Vector3Int node){  
..  
.
}
public List<Vector3Int> FindPath(Tilemap tilemap, Vector3Int start,  
Vector3Int goal){  
...  
}
}
Scripting – Hexagonal Character Movement  
•
Example 3: Character movement in a hexagonal tilemap (+ pathfinding)  
public class HexagonalPlayerController : MonoBehaviour  
{
public Tilemap collisionTilemap;  
public Tilemap worldTilemap;  
public float moveSpeed = 2.5f;  
private Vector3 currentDestination = Vector3.zero;  
private Pathfinding pathfinding;  
private List<Vector3Int> currentPath = null;  
private int pathIndex = 0;  
private Animator animator;  
void Start()  
{
currentDestination = collisionTilemap.CellToWorld(collisionTilemap.  
WorldToCell(transform.position));  
pathfinding = new Pathfinding();  
animator = GetComponent<Animator>();  
}
Scripting – Hexagonal Character Movement  
•
Example 3: Character movement in a hexagonal tilemap (+ pathfinding)  
private void UpdateAnimation(){  
int direction = 0;  
if (currentPath != null){  
float angle = Vector3.Angle(currentDestination – transform.position,  
transform.right);  
if (angle < 10)  
direction = 4; //right  
else if ((angle > 170) && (angle < 190))  
direction = 1; //left  
else if ((angle > 10) && (angle < 90) && (transform.position.y <  
currentDestination.y))  
direction = 3; //top right  
else if ((angle > 90) && (angle < 170) && (transform.position.y <  
currentDestination.y))  
direction = 2; //top left  
else if ((angle > 90) && (angle < 170) && (transform.position.y >  
currentDestination.y))  
direction = 6; //bottom left  
else if ((angle < 90) && (transform.position.y > currentDestination.y))  
direction = 5; //bottom right  
}
animator.SetInteger("Direction", direction);  
}
Scripting – Hexagonal Character Movement  
•
Example 3: Character movement in a hexagonal tilemap (+ pathfinding)  
void Update(){  
if ((Input.GetMouseButton(0)) && (currentPath == null)){  
Vector2 screenPos = Input.mousePosition;  
Vector3 worldPos = Camera.main.GetComponent<Camera>().  
ScreenToWorldPoint(screenPos);  
TileBase tile = collisionTilemap.GetTile(collisionTilemap.  
WorldToCell(worldPos));  
if (tile == null){  
currentPath = pathfinding.FindPath(collisionTilemap,  
collisionTilemap.WorldToCell(transform.position),  
collisionTilemap.WorldToCell(worldPos));  
if (currentPath.Count > 0){  
pathIndex = 0;  
currentDestination = collisionTilemap.  
CellToWorld(currentPath[pathIndex]);  
UpdateAnimation();  
}
}
else{  
currentDestination = Vector3.zero;  
}
}
...  
Scripting – Hexagonal Character Movement  
•
Example 3: Character movement in a hexagonal tilemap (+ pathfinding)  
if ((currentPath != null) && (currentPath.Count > 0)){  
if (pathIndex < currentPath.Count){  
Vector3 targetPosition = new Vector3(currentDestination.x,  
currentDestination.y, transform.position.z);  
transform.position = Vector3.MoveTowards(transform.position,  
targetPosition, moveSpeed * Time.deltaTime);  
if (Vector3.Distance(targetPosition,transform.position)<0.05){  
pathIndex++;  
if (pathIndex < currentPath.Count){  
currentDestination = collisionTilemap.  
CellToWorld(currentPath[pathIndex]);  
UpdateAnimation();  
}
else{  
currentPath = null;  
UpdateAnimation();  
}
}
}
else{  
currentPath = null;  
UpdateAnimation();  
}
}}}  
Exercise 2  
2
) Change the code of hexagonal character movement example  
to use keyboard input to move the zombie character.  
–
The movement must follow the hexagonal directions of the tiles.  
Isometric Tilemaps  
•
Unity also supports Isometric Tilemaps.  
–
The isometric perspective displays all three X, Y, and Z axes, adding a  
pseudo-depth to a Tilemap.  
Isometric Tilemaps  
•
Example: https://shallow-lagoon.itch.io/mega-isometric-block-pack  
1.  
Prepare Sprites for the Isometric Tilemap and create a Tile Pallete.  
Isometric Tilemaps  
•
Example: https://shallow-lagoon.itch.io/mega-isometric-block-pack  
Further Reading  
•
Unity User Manual, Available at: https://docs.unity3d.com/2020.1/  
Documentation/Manual/  
–
2D  
•
•
•
•
•
•
•
•
Tilemap Grid  
Tilemap Renderer  
Tile Assets  
Tile Palette  
Hexagonal Tilemaps  
Isometric Tilemaps  
Painting on Tilemaps  
Tilemap Collider 2D  
•
Books  
–
Jeremy G. (2017). Introduction to Game Design, Prototyping, and  
Development: from the Concept to Playable Game - with Unity and C# (2nd  
ed.). Boston, MA: Addison-Wesley Professional. ISBN: 978-0134659862  
–
Hocking, J. (2018). Unity in Action: Multiplatform Game Development in C#  
(2nd ed.). Shelter Island, NY: Manning Publications. ISBN: 978-1617294969