Part 2 is here. This week we’ll create a generic Entity from where we’ll create all our entities, we’ll skip the original render functions from the original Python version, as won’t be used here, and finally we’ll create a basic map where we’ll be moving around.
Don’t forget to check out the previous parts of the tutorial:
The RogueLike camera
Before anything, one of the little differences with the Python version will be the camera: At the moment when we try to move the player outside of the visible screen the camera doesn’t follow, this is expected as is fixed. We’ll fix this by making the main game camera a child of the player transform. This way when the player moves, the camera will move as well.
The main problem with this is that the main camera is a game object that will be instantiated before the player Entity, this means that we cannot assign the camera to the player when the game starts, as the player instance will not exist at that point.
For fixing this we’ll create a new script and attach it to the main camera:
public class CameraController : MonoBehaviour
{
public Transform target; // The Player Transform
private bool isPlayerFound;
void Start()
{
isPlayerFound = false; // Will be false by default as the Camera is already in the Inspector before the Player Entity is instantiated.
}
private void Update()
{
if (target != null)
{
return; // If the Player has been found means that its Transform != null, so we can get out of the statement
}
else if (target == null && isPlayerFound == false)
{
target = GameObject.FindWithTag("Player").transform; // Find the Player
gameObject.transform.SetParent(target); // Assign the camera game object as a child of the Player Transform
isPlayerFound = true; // Switch the bool so this is not triggered again
}
}
}
You’ll find this snippet in my GitHub as well.
The Map: First attempt via Unity Prefabs.
This part of the code was entirely deprecated in favor of TileMaps (second attempt), but I’ve added this first attempt via prefabs as well for those who are interested. If you’re not interested you can jump directly to “The Map: Second attempt via Unity TileMaps”
The first solution that came to mind was to set a game object for each tile, and construct a map with these. For this you do something like:
public Transform _floorHolder;
public void FloorSetup(int width, int height)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
GameObject _floorTile = Instantiate(floorTile, new Vector3(x, y, 0), Quaternion.identity);
_floorTile.transform.SetParent(_floorHolder);
}
}
}
There’s a few things happening here:
- First you create a variable named
_floorHolder
that will be used as a parent for all floor tile game objects. You’ll want to do something like this to organize your hierarchy, as a simple 80×80 tile map will generate 6400 objects in your Inspector ( only for floor tiles!). - Then we’ll loop through X-width and Y-high in order to generate the declare the necessary vectors to instantiate our game objects.
- Then we’ll instantiate and assign our tile game objects.
- And finally, in the last line of code at
_floorTile.transform.SetParent(_floorHolder);
we’ll add these freshly created tile game objects as children of_floorHolder
, so these won’t clutter the workspace.
The same can be done for Walls, but in this case we cannot just loop through X-width and Y-height as we only want to fill up the map borders:
public void WallsSetup(int width, int height) {
GameObject _test_wallObject = Resources.Load<GameObject>("Prefabs/Wall1");
for (int y = width; y >= -1 ; y--)
{
GameObject _wallTile = Instantiate(_test_wallObject, new Vector3(width, y, 0), Quaternion.identity);
_wallTile.transform.SetParent(_wallHolder);
}
for (int y = width; y >= -1; y--)
{
GameObject _wallTile = Instantiate(_test_wallObject, new Vector3(-1, y, 0), Quaternion.identity);
_wallTile.transform.SetParent(_wallHolder);
}
for (int x = -1; x < height; x++)
{
GameObject _wallTile = Instantiate(_test_wallObject, new Vector3(x, -1, 0), Quaternion.identity);
_wallTile.transform.SetParent(_wallHolder);
}
for (int x = -1; x < height; x++)
{
GameObject _wallTile = Instantiate(_test_wallObject, new Vector3(x, height, 0), Quaternion.identity);
_wallTile.transform.SetParent(_wallHolder);
}
}
Once this is done we’ll have a new map, where each tile (floor, or wall) is a different game object. This comes with problems as well, as we’ll be using physic objects with physic bodies and colliders, new issues will be introduced:

We’ll fix this behaviour by switching our walls act as static rigid bodies and a bit of code:
private Vector3 _lastKnownPlayerPosition; // We use this to track players last position before their next move
public Void MovePlayer(string direction){
_lastKnownPlayerPosition = player.transform.localPosition;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Wall")
{
player.transform.position = new Vector3(_lastKnownPlayerPosition.x, _lastKnownPlayerPosition.y, 0);
}
}
This part checks if the player has touched a wall, if this happens then “teleports” the player to its last known position. The reason for this is because when the player collides with the wall, physics come into play and the player vector will end un-aligned (no longer in full-integer vectors, but in floating points) breaking the whole game logic afterwards as movement relies on integer vectors. Most likely this also means that enemies or other entities that have a rigid body will have to go through the same bit of code, but that’s a problem for the future at this point.
The Map: Second attempt via Unity TileMaps.
“Pre-optimization is the root of all evil”. Yes, I agree, and in my short experience sometimes I know I’ve lost too much time optimizing something that didn’t need any optimization to start with, but this is not the case here. This implementation is not only way more efficient than using prefabs, but also seems to be the best tool for the work:
First: How do we know that to use TileMaps is more performant than Prefabs? In this case I used Stopwatch()
and Unity.Diagnostics
to get some data about how much time did each method need to complete the map creation. Checking how long it takes to create a simple 106x30tiles grid via Prefabs vs via Tilemaps outputted 200ms vs 14 ms respectively.
Just generating the floor map is already 15 times faster by using TileMaps, and while 200ms or 14ms is not that much for the human eye, the difference will definitely make an impact as we move forward and more ingredients are added to the stew. For example adding the walls to the map just added 2 additional ms, while Prefabs was almost almost doubling the initial calculation and reaching 400ms, that’s already 25 times faster once walls come into play.

Running Unity.diagnostics
This is the bit of code I ran to check how long the methods will need from start to finish:
using System.Diagnostics; // enables System.Diagnostics.StopWatch
using System; // enables System.TimeSPan
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
FloorSetup(106, 30); // Method we want to track
WallsSetup(106, 30); // Method we want to track
stopwatch.Stop();
TimeSpan ts = stopwatch.Elapsed;
int _ms = ts.Milliseconds; // <- Set a breakpoint here + run the debugger.
Remember to set a breakpoint in Visual Studio (or your IDE) at int _ms
, so you’ll be able to check the data.
After checking the docs and a few random tutorials on YouTube, I got some general ideas of how TileMaps work:
- You can create directly in your hierarchy a 2D TileMap game object, this will generate a Grid object automatically with some components (Transform, Grid) that will act as something like a TileMap manager. Seems to work a bit like the UI canvas elements, where your canvas UI elements are children of Canvas, and if you try to create directly an UI element in your hierarchy, the canvas parent object is created by default. The same applies here: Your TileMap elements will be children of this Grid object.

- This Grid object has a default child object called TileMap, consider this a type of a layer, where you can stack TileMaps on top of each other. Each TileMap has a bunch of components (Transform, TileMap, TileMap Renderer), but and more can be added like for example a TileMap collisions 2D component.
- In Unity > Window > Tile Palette, is where we find all the Tile painting tools we’ll use to work with tiles. When we “create a palette” where basically creating a collection of tiles where we can choose from at any given time. Each palette is stored as an object in your project


Creating our Floor via TileMaps
At this point we only have to adapt the previous code we have written for Prefabs, and use TileMaps instead:
public class GridGenerator : MonoBehaviour
{
public Tilemap floorMap;
public Tile floorTile;
public void Start()
{
FloorSetup(width, height);
}
public void FloorSetup(int width, int height)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
floorMap.SetTile(new Vector3Int(x, y, 0), floorTile);
}
}
}
}
Creating our Walls via TileMaps
The same concept than our previous Prefab example will apply here, we’ll only fill up the borders of the map:
public class GridGenerator : MonoBehaviour
{
public Tilemap wallMap;
public Tile wallTile;
public void Start()
{
WallSetup(width, height);
}
public void WallSetup(int width, int height)
{
for (int y = width; y >= -1; y--)
{
wallMap.SetTile(new Vector3Int(width, y, 0), wallTile);
}
for (int y = width; y >= -1; y--)
{
wallMap.SetTile(new Vector3Int(-1, y, 0), wallTile);
}
for (int x = -1; x < height; x++)
{
wallMap.SetTile(new Vector3Int(x, -1, 0), wallTile);
}
for (int x = -1; x < height; x++)
{
wallMap.SetTile(new Vector3Int(x, height, 0), wallTile);
}
}
}
Tilemap collisions
We’ll see that the initial problem we had with Prefabs, where our player would ignore physical bodies, will apply here again. With Prefabs we solved this by adding a collision body to the walls, and with TileMaps will be no different, but in this case we need to use a different collider and put the walls in a different TileMap as well, or our colliders would affect the floor tiles too, which we don’t want:


And is working again, with the difference that now is much more faster and performant:

You can see in the previous GIF that there’s a little oddity: The player (or any Entity) is spawned not in the center of the tiles, but in the junction of these. As our TileMap is generated from (0,0,0) that will be the left bottom corner of our first tile, when we generate entities in non-float positions we’ll see that these are aligned perfectly with the tiles, which is something we do not want in this scenario.
To resolve this final issue we’ll create a new function within Entity.cs
that “reallocates” our entities to fit nicely in our map:
Vector3 reallocateEntity(int ax, int ay)
{
return new Vector3(ax + 0.5f, ay + 0.5f, 0);
}
entityLocation = reallocateEntity(aX, aY);
This way our entities will show in the center of our tiles.
And that’s all for this week! Do not miss the next week tutorial, where we’ll be changing our Map functions to create levels procedurally, instead of playing the current square-shaped level:

Leave a Reply