Building Games That Think in Squares: Your Guide to Unity Grid-Based System Development
Here's the thing—when I was working on my first strategy game at CMU, I spent way too long trying to make units move "naturally" with continuous positioning. Everything was floaty and imprecise. Players couldn't tell where units would end up, collision detection was a nightmare, and pathfinding? Forget about it. Then my mentor sat me down and said, "Mayank, stop fighting against structure. Your game wants to be grid-based."
That conversation changed everything. A Unity grid-based system solves the problem of organizing game space into a structured, predictable, and easily manageable format. It allows you to create games where movement, placement, and interaction happen in discrete, well-defined steps, which is the foundation for genres like strategy games, puzzle games, tower defense, and tactical RPGs. Think of it as digital graph paper or a chessboard—it provides a clear and logical framework for both the developer to build upon and for the player to understand and interact with, ensuring that all game elements align perfectly and predictably within the game world.
What Makes Grids So Powerful for Game Development
Let me break down why grids are such a game-changer for certain types of projects. When you're building strategy games, puzzle games, or anything where precise positioning matters, grids give you superpowers.
Without a grid system Unity implementation, you're dealing with continuous floating-point positions. That means a unit could be at position (3.7214, 1.9832), and another at (3.7198, 1.9847). Are those overlapping? Are they neighbors? Should they interact? It's chaos.
With a Unity grid-based system, those questions disappear. A unit is either in cell (3, 1) or it isn't. Two units are neighbors or they're not. Pathfinding goes from "calculate angles and distances between thousands of points" to "check which adjacent cells are walkable." The clarity this provides is incredible for both your code and your players' understanding of the game.
I've seen this technique used brilliantly in games like Fire Emblem, Civilization, and even Pokémon. The grid isn't just a technical convenience—it's a design language that players intuitively understand.
The Essential Building Blocks You Need to Understand
Before we dive into implementation, let's get our terminology straight. When I was learning this stuff at CMU, these concepts seemed overwhelming, but they're actually pretty straightforward once you see how they fit together.
Grid: A data structure, typically a 2D or 3D array, that represents the game world as a collection of uniformly sized cells, providing a logical coordinate system separate from the world coordinates. Think of it as your underlying data layer—the truth about what exists where.
Cell/Tile: A single, discrete unit within a grid that can hold data, such as its type (e.g., "grass," "wall"), the object it contains, or its status in a pathfinding calculation. This is your atomic unit of game space.
Tilemap: Unity's built-in system for creating 2D levels by painting tiles onto a grid, which is highly optimized for rendering and collision of grid-based worlds. This is Unity doing the heavy lifting for you.
World Position: The standard floating-point Vector3 coordinate of an object in Unity's global scene space, which is continuous and not constrained to a grid. This is where your GameObjects actually live in 3D space.
Grid Position / Cell Coordinate: The integer-based coordinate (e.g., Vector3Int) that identifies a specific cell's location within the grid's own logical structure, such as (5, 2). These are your "chess notation" coordinates.
Pathfinding: The algorithmic process of calculating the most efficient route between two points on a grid, intelligently navigating around any cells that are marked as obstacles. This is how your AI knows where to go.
A* (A-Star): A highly efficient and widely used pathfinding algorithm that finds the shortest path between two nodes by evaluating a combination of the cost from the start and a heuristic estimate to the end. This is the industry-standard pathfinding approach—I use it in almost every project with AI movement.
Node: In the context of pathfinding, a node is an object that represents a cell on the grid, containing crucial information like its position, its walkability, and its costs for the A* pathfinding Unity algorithm.
How to Convert Between World Space and Grid Space
This is where things get practical. The magic of grid systems Unity relies on converting between two coordinate systems: world space (where Unity objects actually exist) and grid space (your logical game structure).
Custom Grid Data Structure: At its heart, a grid is just a data structure you create in code. A 2D array is the most common way to represent a dense grid where every cell needs to store some information.
// A simple grid of integers with a width of 10 and a height of 5
int[,] gridArray = new int[10, 5];
World-to-Cell Conversion: This is the crucial process of taking a continuous world position (like a mouse click) and finding the corresponding integer-based cell coordinate on the grid. Unity's Grid component simplifies this.
// Assuming 'grid' is a reference to the Grid component
Vector3 worldPosition = new Vector3(3.7f, 1.2f, 0f);
Vector3Int cellPosition = grid.WorldToCell(worldPosition); // Result would be (3, 1, 0)
See how that works? Your player clicks at some arbitrary world position, and you instantly know which cell they clicked on. This one technique alone has saved me countless hours of manual math.
Cell-to-World Conversion: This is the reverse process, used to find the world position for a given grid cell, which is essential for placing objects or characters precisely in the center of a tile.
// Get the world position of the center of cell (3, 1, 0)
Vector3 cellCenterWorld = grid.GetCellCenterWorld(new Vector3Int(3, 1, 0));
Unity's Tilemap System: This is a high-level, visual system for 2D grids. It uses a Grid component for layout, a Tilemap component for a layer of tiles, and Tile assets that define what to draw.
// To programmatically place a tile, you need a reference to the Tilemap and the Tile asset
tilemap.SetTile(new Vector3Int(5, 3, 0), wallTile);
Verified: Unity Docs - Scripting API: GridLayout
Unity's Built-In Tilemap vs Rolling Your Own Custom Grid
Actually, wait—before we go further, you need to understand when to use Unity's Tilemap programming tools versus when to build a custom grid Unity structure. I spent a good afternoon debugging a project before I realized I was using the wrong approach for what I needed.
Here's a comparison table that would have saved me that time:
| Criteria | Approach A: Unity's Tilemap System | Approach B: Custom Grid Class |
|---|---|---|
| Best For | 2D level design, visual editing, and games where the grid is the primary visual element (e.g., platformers, top-down RPGs). | Abstract data grids, 3D grids, pathfinding graphs, and systems where the grid logic is separate from the visuals (e.g., strategy games). |
| Performance | Highly optimized for rendering and 2D physics thanks to the Tilemap Renderer and Tilemap Collider 2D. |
Performance depends entirely on your implementation; it can be very fast for logic but requires manual optimization for rendering. |
| Complexity | Easier for visual setup and basic interaction, but can become complex to manage purely through code. | Requires more initial setup code, but offers complete programmatic control and flexibility over the data stored in each cell. |
| Code Example | tilemap.SetTile(cellPos, myTile); |
customGrid.SetValue(x, y, someObject); |
For most beginner 2D projects, I always recommend starting with Unity's Tilemap. It's battle-tested, optimized, and integrates beautifully with the editor. But when you're building 3D strategy games, complex pathfinding systems, or anything where the grid is primarily data rather than visuals, a custom grid Unity class gives you the control you need.
Why Grid Systems Transform Your Game Architecture
Here's why I'm so passionate about teaching grid-based systems—they fundamentally change how you approach game development:
- Structured Level Design: Grids provide a clean, organized, and efficient workflow for designing levels, especially for games with tiled environments. Your level design becomes like LEGO—snapping pieces together instead of freeform sculpting.
- Simplified Gameplay Logic: Discrete movement, turn-based mechanics, and placement rules (like in tower defense) become much simpler to program when based on a grid. Instead of checking distances and angles constantly, you're just checking array indices.
- Foundation for AI Pathfinding: A logical grid is the first step and a fundamental requirement for implementing pathfinding algorithms like A*, allowing enemies to navigate complex environments. Without a grid, pathfinding becomes exponentially more complex.
- Performance Optimization: For 2D games, Unity's Tilemap system automatically handles sprite batching and collider optimization, leading to significant performance gains over using individual GameObjects. I've seen frame rates jump from 30 FPS to 60 FPS just by switching from individual sprites to Tilemaps.
The Pro Tips That Saved My Projects
Let me share some hard-earned wisdom that'll save you from the mistakes I made. These are the techniques I use in every grid-based project now.
Separate Logic from Visuals: Your core grid data (what's in each cell) should be in a custom class, separate from the visual representation (like a Tilemap or instantiated GameObjects). This makes your system more flexible.
// Your grid class should be a plain C# object, not a MonoBehaviour
public class GameGrid<T> { private T[,] gridArray; }
From a developer's perspective, what makes this brilliant is the separation of concerns. Your game logic doesn't care whether you're rendering with Tilemaps, 3D models, or ASCII characters—it just knows the grid state.
Use a Generic Grid Class: Create a generic grid class (Grid<T>) so you can reuse it for different purposes, whether it's storing integers, booleans, or complex custom objects in each cell.
// This allows you to create grids for pathfinding, resources, units, etc.
Grid<PathNode> pathfindingGrid = new Grid<PathNode>(20, 10);
I've found this works best when you realize you need multiple grids in one game. One grid for terrain, one for units, one for fog of war—all using the same underlying class.
Use Scriptable Objects for Tile Data: Instead of storing simple tile types, use Scriptable Objects to define your tiles. This allows you to easily add more data like walkability, resource type, or special rules.
// A ScriptableObject can hold all the data for a specific tile type
[CreateAssetMenu] public class TileData : ScriptableObject { public bool isWalkable; public int movementCost; }
Verified: Unity Learn - Working with Tilemaps
Trust me, you'll thank me later for this tip. When you need to add one more property to your tiles (and you will), you'll be glad it's in a ScriptableObject instead of hardcoded everywhere.
How The Pros Use Grids (Stardew Valley, Into the Breach, Clash of Clans)
Let me tell you about how some of my favorite Unity tile-based game implementations work—I've studied these extensively, and they're brilliant examples of grid systems in action.
One of My Favorite Implementations Is in Stardew Valley
The Mechanic: The entire farm is a grid where the player can till soil, plant seeds, and place objects like sprinklers and fences.
The Implementation: A data grid likely stores the state of each tile (tilled, watered, crop type). The player's actions convert world positions to grid coordinates to modify the state of the underlying data grid, which then updates the visual Tilemap.
The Player Experience: The grid provides a clear, satisfying structure for building and optimizing the farm layout, turning farm design into a strategic puzzle.
What I find fascinating about this approach is how invisible the grid feels while playing. You're not consciously thinking "I'm placing this in cell (12, 7)"—you're thinking "this sprinkler covers these crops." That's excellent grid design.
Here's How Into the Breach Solved This Exact Problem
The Mechanic: A turn-based strategy game where mechs and aliens fight on an 8x8 grid. All movement, attacks, and environmental effects are strictly confined to the grid.
The Implementation: The core of the game is a custom grid Unity class holding a Unit object or null for each cell. Pathfinding and attack ranges are calculated based on Unity grid coordinates, and all actions are resolved within this logical grid.
The Player Experience: The strict grid system removes ambiguity and makes the game a pure tactical puzzle, where every move is a calculated decision with clear consequences.
After analyzing dozens of games, this stands out because it embraces the grid completely. There's no attempt to hide it or make things feel "smooth"—the grid IS the game, and that clarity is what makes it so compelling.
I've Seen This Technique Used Brilliantly in Clash of Clans
The Mechanic: Players build a village by placing structures of various sizes (e.g., 2x2, 3x3) onto a large grid. These structures cannot overlap.
The Implementation: A 2D array likely stores whether each cell is occupied. When a player tries to place a building, the game checks all the required cells in the grid array to see if they are available before allowing the placement.
The Player Experience: The grid system Unity makes base-building intuitive and strategic, as players must think carefully about the placement and spacing of their structures to create an effective defense.
Here's how you can adapt this for your own game: when checking multi-cell objects, iterate through all the cells that object would occupy and verify they're all available before allowing placement. Simple but powerful.
Three Complete Implementations From Scratch
Let me show you how I approach this in my own projects. I'm going to walk you through three different implementations that cover the most common use cases. We're going to start with a custom grid from scratch, move to grid-based movement Unity with Tilemaps, and finish with A star pathfinding Unity.
Blueprint 1: Custom Generic Grid & Object Placement
A. Scenario Goal:
Create a reusable, generic grid class from scratch and use it to place prefabs in the world by clicking the mouse.
B. Unity Editor Setup:
- An empty GameObject named
GridManager. - A simple prefab (like a Cube or Sprite) to be placed on the grid.
- Ensure your Main Camera has the "MainCamera" tag assigned (this is required for
Camera.mainto work).
C. Step-by-Step Code Implementation:
When I'm working on projects, I always start with a solid, reusable grid class. Here's the exact method I use:
// 1. First, create a generic, reusable Grid class. This is not a MonoBehaviour.
public class Grid<TGridObject>
{
private int width;
private int height;
private float cellSize;
private TGridObject[,] gridArray;
public Grid(int width, int height, float cellSize)
{
this.width = width;
this.height = height;
this.cellSize = cellSize;
gridArray = new TGridObject[width, height];
}
// 2. Create a method to convert a world position to grid coordinates.
public void GetXY(Vector3 worldPosition, out int x, out int y)
{
x = Mathf.FloorToInt(worldPosition.x / cellSize);
y = Mathf.FloorToInt(worldPosition.y / cellSize);
}
// 3. Create a method to set a value in a grid cell.
public void SetValue(int x, int y, TGridObject value)
{
if (x >= 0 && y >= 0 && x < width && y < height)
{
gridArray[x, y] = value;
}
}
// Add these methods to your Grid<TGridObject> class
public int GetWidth()
{
return width;
}
public int GetHeight()
{
return height;
}
public TGridObject GetValue(int x, int y)
{
if (x >= 0 && y >= 0 && x < width && y < height)
{
return gridArray[x, y];
}
return default(TGridObject);
}
}
// 4. Now, create a MonoBehaviour to manage and interact with the grid.
public class GridManager : MonoBehaviour
{
private Grid<bool> grid; // A grid of booleans to track occupied cells.
public GameObject objectToPlace;
void Start()
{
grid = new Grid<bool>(10, 5, 2.0f);
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
// 5. Get the mouse position in world space.
Vector3 mouseWorldPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mouseWorldPos.z = 0;
// 6. Convert the world position to grid coordinates.
grid.GetXY(mouseWorldPos, out int x, out int y);
// 7. Place the object and mark the cell as occupied.
Instantiate(objectToPlace, new Vector3(x * 2.0f + 1.0f, y * 2.0f + 1.0f, 0), Quaternion.identity);
grid.SetValue(x, y, true);
}
}
}
This gives you a complete custom grid system that you can adapt for any project. In my projects, I use this pattern constantly because it's so flexible—you can store literally anything in those cells.
Blueprint 2: 2D Player Movement on a Tilemap
A. Scenario Goal:
Create a simple 2D level using Unity's Tilemap and allow a player to move one tile at a time, using Scriptable Objects to define tile properties.
B. Unity Editor Setup:
- Create a 2D Tilemap (
GameObject -> 2D Object -> Tilemap). - Create the two scripts below:
TileData.csandCustomTile.cs. - Create two
TileDataassets (Right-click in Project -> Create -> Tile Data): one for "Floor" (isWalkable = true) and one for "Wall" (isWalkable = false). - Create two
CustomTileassets (Right-click in Project -> Create -> Tiles -> Custom Tile): one for "Floor" and one for "Wall". Assign the correspondingTileDataassets and a sprite to each. - Use the Tile Palette to paint a level using your new Floor and Wall tiles.
- Create a 2D Sprite for the player and place it on a floor tile.
- Attach the
TilemapMovementscript below to the player sprite. - In the Inspector, drag your Tilemap GameObject into the
Tilemapfield of the script.
C. Step-by-Step Code Implementation:
Let's tackle this together. For 2D implementations, my process uses Unity's programmatic Tilemap Unity system because it handles all the rendering optimization for you. We'll also implement the Scriptable Object tip here for a more robust system.
// 1. First, create the ScriptableObject for your tile data.
// Create a new C# script called TileData.cs
using UnityEngine;
[CreateAssetMenu(fileName = "New TileData", menuName = "Tile Data")]
public class TileData : ScriptableObject
{
public bool isWalkable;
public int movementCost;
}
// 2. Next, create a custom Tile class that uses this data.
// Create a new C# script called CustomTile.cs
using UnityEngine;
using UnityEngine.Tilemaps;
[CreateAssetMenu(fileName = "New CustomTile", menuName = "Tiles/Custom Tile")]
public class CustomTile : Tile
{
public TileData tileData;
}
// 3. Now, create the player movement script.
// Create a new C# script called TilemapMovement.cs
using UnityEngine;
using UnityEngine.Tilemaps;
public class TilemapMovement : MonoBehaviour
{
public Tilemap tilemap;
private Grid grid;
private Vector3Int playerCellPos;
void Start()
{
// 4. Get the parent Grid component from the Tilemap.
grid = tilemap.layoutGrid;
// 5. Convert the player's starting world position to a cell position.
playerCellPos = grid.WorldToCell(transform.position);
// 6. Snap the player to the center of the starting cell.
transform.position = grid.GetCellCenterWorld(playerCellPos);
}
void Update()
{
Vector3Int targetCell = playerCellPos;
// 7. Check for keyboard input to determine the target cell.
if (Input.GetKeyDown(KeyCode.W)) targetCell += Vector3Int.up;
if (Input.GetKeyDown(KeyCode.A)) targetCell += Vector3Int.left;
if (Input.GetKeyDown(KeyCode.S)) targetCell += Vector3Int.down;
if (Input.GetKeyDown(KeyCode.D)) targetCell += Vector3Int.right;
// 8. Get the custom tile at the target cell position.
CustomTile targetTile = tilemap.GetTile<CustomTile>(targetCell);
// 9. Check if the tile exists and its data says it's walkable.
if (targetTile != null && targetTile.tileData.isWalkable)
{
// 10. If it's a valid move, update the player's cell position and world position.
playerCellPos = targetCell;
transform.position = grid.GetCellCenterWorld(playerCellPos);
}
}
}
I've found this works best when you keep movement simple and snappy. Players expect tile-based movement to be instant and precise—don't try to add smoothing or animation in the movement logic itself. Handle that separately with visual tweening if you want smooth transitions.
Blueprint 3: Basic A* Pathfinding
A. Scenario Goal:
Implement a simple A* algorithm to find a path between a start and end node on a custom grid, avoiding obstacles.
B. Unity Editor Setup:
- Use the
GridManagersetup from Blueprint 1. - Manually mark some cells in the grid as "obstacles".
C. Step-by-Step Code Implementation:
For 3D implementations or complex AI, my process includes implementing the A* pathfinding Unity algorithm. These are the exact settings I've configured dozens of times:
using System.Collections.Generic;
using UnityEngine;
// 1. First, create a Node class to hold pathfinding data for each grid cell.
public class PathNode
{
public int x;
public int y;
public int gCost; // Cost from the start node.
public int hCost; // Heuristic cost to the end node.
public int fCost; // gCost + hCost.
public bool isWalkable;
public PathNode parent;
public PathNode(int x, int y)
{
this.x = x;
this.y = y;
isWalkable = true;
}
public void CalculateFCost()
{
fCost = gCost + hCost;
}
}
// 2. In a new Pathfinding class, create the main algorithm.
public class Pathfinding
{
private Grid<PathNode> grid;
private List<PathNode> openList;
private List<PathNode> closedList;
private const int MOVE_STRAIGHT_COST = 10;
private const int MOVE_DIAGONAL_COST = 14;
public Pathfinding(int width, int height)
{
// Create a grid and populate it with PathNode objects
grid = new Grid<PathNode>(width, height, 1f);
// Initialize all nodes in the grid
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
grid.SetValue(x, y, new PathNode(x, y));
}
}
}
public List<PathNode> FindPath(int startX, int startY, int endX, int endY)
{
PathNode startNode = grid.GetValue(startX, startY);
PathNode endNode = grid.GetValue(endX, endY);
openList = new List<PathNode> { startNode };
closedList = new List<PathNode>();
// Initialize all node costs
int gridWidth = grid.GetWidth();
int gridHeight = grid.GetHeight();
for (int x = 0; x < gridWidth; x++)
{
for (int y = 0; y < gridHeight; y++)
{
PathNode pathNode = grid.GetValue(x, y);
pathNode.gCost = int.MaxValue;
pathNode.CalculateFCost();
pathNode.parent = null;
}
}
startNode.gCost = 0;
startNode.hCost = CalculateDistanceCost(startNode, endNode);
startNode.CalculateFCost();
// 3. The main A* loop.
while (openList.Count > 0)
{
PathNode currentNode = GetLowestFCostNode(openList);
if (currentNode == endNode)
{
return CalculatePath(endNode); // Path found!
}
openList.Remove(currentNode);
closedList.Add(currentNode);
// 4. Check all neighbors.
foreach (PathNode neighbourNode in GetNeighbourList(currentNode))
{
if (closedList.Contains(neighbourNode)) continue;
if (!neighbourNode.isWalkable)
{
closedList.Add(neighbourNode);
continue;
}
int tentativeGCost = currentNode.gCost + CalculateDistanceCost(currentNode, neighbourNode);
if (tentativeGCost < neighbourNode.gCost)
{
// 5. If this is a better path, update the node's data.
neighbourNode.parent = currentNode;
neighbourNode.gCost = tentativeGCost;
neighbourNode.hCost = CalculateDistanceCost(neighbourNode, endNode);
neighbourNode.CalculateFCost();
if (!openList.Contains(neighbourNode))
{
openList.Add(neighbourNode);
}
}
}
}
return null; // No path found.
}
// 6. Helper method to get the list of neighboring nodes.
private List<PathNode> GetNeighbourList(PathNode currentNode)
{
List<PathNode> neighbourList = new List<PathNode>();
// Check all 8 directions (including diagonals)
if (currentNode.x - 1 >= 0)
{
// Left
neighbourList.Add(grid.GetValue(currentNode.x - 1, currentNode.y));
// Left Down
if (currentNode.y - 1 >= 0)
neighbourList.Add(grid.GetValue(currentNode.x - 1, currentNode.y - 1));
// Left Up
if (currentNode.y + 1 < grid.GetHeight())
neighbourList.Add(grid.GetValue(currentNode.x - 1, currentNode.y + 1));
}
if (currentNode.x + 1 < grid.GetWidth())
{
// Right
neighbourList.Add(grid.GetValue(currentNode.x + 1, currentNode.y));
// Right Down
if (currentNode.y - 1 >= 0)
neighbourList.Add(grid.GetValue(currentNode.x + 1, currentNode.y - 1));
// Right Up
if (currentNode.y + 1 < grid.GetHeight())
neighbourList.Add(grid.GetValue(currentNode.x + 1, currentNode.y + 1));
}
// Down
if (currentNode.y - 1 >= 0)
neighbourList.Add(grid.GetValue(currentNode.x, currentNode.y - 1));
// Up
if (currentNode.y + 1 < grid.GetHeight())
neighbourList.Add(grid.GetValue(currentNode.x, currentNode.y + 1));
return neighbourList;
}
// 7. Helper method to find the node with the lowest fCost.
private PathNode GetLowestFCostNode(List<PathNode> pathNodeList)
{
PathNode lowestFCostNode = pathNodeList[0];
for (int i = 1; i < pathNodeList.Count; i++)
{
if (pathNodeList[i].fCost < lowestFCostNode.fCost)
{
lowestFCostNode = pathNodeList[i];
}
}
return lowestFCostNode;
}
// 8. Helper method to calculate the distance cost between two nodes.
private int CalculateDistanceCost(PathNode a, PathNode b)
{
int xDistance = Mathf.Abs(a.x - b.x);
int yDistance = Mathf.Abs(a.y - b.y);
int remaining = Mathf.Abs(xDistance - yDistance);
return MOVE_DIAGONAL_COST * Mathf.Min(xDistance, yDistance) + MOVE_STRAIGHT_COST * remaining;
}
// 9. Helper method to reconstruct the path by following parent nodes.
private List<PathNode> CalculatePath(PathNode endNode)
{
List<PathNode> path = new List<PathNode>();
path.Add(endNode);
PathNode currentNode = endNode;
while (currentNode.parent != null)
{
path.Add(currentNode.parent);
currentNode = currentNode.parent;
}
path.Reverse();
return path;
}
// Public method to set a node as walkable or unwalkable (for obstacles)
public void SetWalkable(int x, int y, bool isWalkable)
{
grid.GetValue(x, y).isWalkable = isWalkable;
}
}
// 10. To use the Pathfinding class, create a test MonoBehaviour.
public class PathfindingTester : MonoBehaviour
{
private Pathfinding pathfinding;
void Start()
{
// Create a 10x10 grid for pathfinding
pathfinding = new Pathfinding(10, 10);
// Set some obstacles
pathfinding.SetWalkable(3, 4, false);
pathfinding.SetWalkable(4, 4, false);
pathfinding.SetWalkable(5, 4, false);
// Find a path from (0,0) to (9,9)
List<PathNode> path = pathfinding.FindPath(0, 0, 9, 9);
if (path != null)
{
// Draw lines in the scene view to visualize the path
for (int i = 0; i < path.Count - 1; i++)
{
Debug.DrawLine(new Vector3(path[i].x, path[i].y) * 1f + Vector3.one * 0.5f,
new Vector3(path[i + 1].x, path[i + 1].y) * 1f + Vector3.one * 0.5f,
Color.green, 100f);
}
}
else
{
Debug.Log("No path found!");
}
}
}
Verified: Unity Docs - Tilemaps
I've implemented this Unity pathfinding algorithm in probably a dozen projects. The key insight is that A* is really just "explore the most promising paths first." The fCost tells you which node is most promising, combining how far you've already traveled (gCost) with an estimate of how far you still need to go (hCost).
What You'll Walk Away With
After implementing these Unity grid-based system techniques in your own project, here's what you're going to gain:
Main Benefits:
- A solid understanding of how to structure game space for strategy, puzzle, and tactical games
- The ability to implement both Unity's Tilemap system and custom grid structures depending on your needs
- A working A* pathfinding implementation that you can adapt to any grid-based game
- The confidence to tackle turn-based mechanics, placement systems, and discrete movement
Practical Advantages:
- Your levels become easier to design and iterate on with clear, predictable structure
- AI pathfinding becomes dramatically simpler and more efficient
- Performance improves significantly for 2D games using Unity's optimized Tilemap system
- Players get clearer, more intuitive interactions with your game world
Skills You've Developed:
- Working with 2D arrays and generic data structures
- Converting between world space and grid space coordinate systems
- Implementing classic algorithms like A* for pathfinding
- Understanding when to use built-in Unity systems versus custom solutions
Your Next Steps
Now that you've got the foundation, here's what I recommend:
For Beginners:
- Start with Blueprint 1 and get comfortable with the custom grid class
- Experiment with different cell sizes and grid dimensions
- Try storing different types of data in your grid (numbers, enums, custom objects)
Learning Path:
- Move on to Blueprint 2 and explore Unity's Tilemap programming tools
- Implement simple fog of war by tracking which cells have been "revealed"
- Add diagonal movement to your grid-based movement Unity system
Next Exploration Topics:
- Explore Unity's Rule Tile system for automatic tile connections
- Investigate more advanced pathfinding like Jump Point Search for performance
- Look into hex grids for different strategic possibilities
- Study influence maps for more sophisticated AI decision-making
Wrapping Up: You've Got the Power to Build Strategic Masterpieces
We've covered everything from the fundamental concept of Unity grid-based system architecture to three complete, production-ready implementations. You now have the knowledge to transform vague spatial concepts into precise, predictable game spaces that players can understand and strategize within.
The beauty of grid systems is how they simplify complexity. Instead of worrying about floating-point precision, angle calculations, and continuous collision detection, you're thinking in discrete, logical units. It's like the difference between sculpting freeform clay and building with LEGO—both have their place, but grids give you structure and speed.
Remember—some of the most successful games of all time are grid-based. From Pokémon to Civilization to Into the Breach, grids provide a foundation for deep strategic gameplay. Start with the implementations I've shown you, adapt them to your vision, and build something amazing.
Ready to Start Building Your First Game?
If you've enjoyed learning about grid systems and want to take your Unity skills to the next level, I've designed a complete course that takes you from absolute basics to building professional game experiences.
Check out the Mr. Blocks course on Outscal where we build a complete game from scratch, covering not just grid system Unity techniques but player mechanics, level design, AI implementation, optimization, and everything else you need to ship your first game.
You'll go from "I want to make games" to "I built this game" in a structured, hands-on way. No fluff, no theory for theory's sake—just practical game development skills you'll use in every project.
Key Takeaways
- A Unity grid-based system organizes game space into discrete, uniformly sized cells, providing a predictable framework perfect for strategy games, puzzle games, and tactical RPGs
- Grids use two coordinate systems: world position (continuous Vector3 for rendering) and grid position (discrete Vector3Int for logic)—converting between them is fundamental
- Unity's Tilemap system is best for 2D visual level design with optimized rendering, while custom grid classes excel at abstract data, 3D grids, and pathfinding
- Separate your grid logic from visuals by storing game state in a custom grid class, independent of how you render it (Tilemaps, 3D models, or sprites)
- Generic grid classes (
Grid<T>) are reusable across different purposes—pathfinding, unit placement, resource tracking, fog of war—all from one flexible structure - A* pathfinding relies on fCost which combines gCost (distance from start) and hCost (estimated distance to goal) to efficiently find the shortest path
- Use ScriptableObjects for tile data to keep tile properties flexible and designer-friendly without hardcoding values throughout your codebase
- Grid systems simplify AI dramatically by reducing pathfinding from continuous space calculations to checking adjacent cells in an array
Common Questions
What is a Unity grid-based system and when should I use it?
A Unity grid-based system is a data structure that divides game space into uniform cells, perfect for strategy games, puzzle games, tower defense, and tactical RPGs where discrete movement and placement are important. Use it when you need precise positioning, turn-based mechanics, or AI pathfinding.
What's the difference between world position and grid position in Unity?
World position is a continuous floating-point Vector3 coordinate in Unity's scene space (like 3.7, 1.2, 0). Grid position is a discrete integer Vector3Int coordinate identifying a specific cell (like 3, 1, 0). You convert between them using grid.WorldToCell() and grid.GetCellCenterWorld() methods.
Should I use Unity's Tilemap system or build a custom grid class?
Use Unity's Tilemap programming system for 2D games where the grid is visual (platformers, top-down games) because it's highly optimized for rendering and collision. Build a custom grid Unity class for 3D games, abstract data structures, pathfinding systems, or when you need complete programmatic control over grid data.
How do I convert a mouse click to a grid cell position?
First convert the mouse screen position to world space with Camera.main.ScreenToWorldPoint(Input.mousePosition), then convert the world position to grid coordinates using grid.WorldToCell(worldPosition). This gives you the cell the player clicked on.
What is A* pathfinding and how does it work on grids?
A star pathfinding Unity is an algorithm that finds the shortest path between two grid cells by evaluating nodes based on fCost (gCost + hCost). gCost is the distance from the start, hCost estimates distance to the goal. It explores the most promising paths first, efficiently navigating around obstacles.
How do I make a generic grid class in Unity?
Create a class Grid<TGridObject> with a 2D array TGridObject[,] to store any type of data. Include methods for GetValue(x, y), SetValue(x, y, value), and coordinate conversion. This lets you create Grid<bool>, Grid<PathNode>, Grid<Unit>, etc., all using the same reusable class.
Why should I separate grid logic from visual representation?
Separating logic from visuals makes your code more flexible and maintainable. Your grid data (what's in each cell) lives in a plain C# class, while rendering (Tilemap, 3D models, sprites) is handled separately. This means you can change how things look without touching game logic, or run headless simulations for AI testing.
How do I handle multi-cell objects like 2x2 buildings in a grid?
When placing a multi-cell object, iterate through all cells it would occupy and check if they're all available. For a 2x2 building at position (x, y), check cells (x, y), (x+1, y), (x, y+1), and (x+1, y+1). Only allow placement if all cells are free, then mark all as occupied.
What's the difference between Tilemap and Grid components in Unity?
The Grid component defines the layout (cell size, orientation) and handles coordinate conversion. The Tilemap component is a layer that paints tiles onto that grid. You can have multiple Tilemaps (layers) sharing one Grid, like separate layers for ground, decorations, and collision.
How do I implement grid-based movement Unity for a player character?
Store the player's current cell position as Vector3Int. On input, calculate the target cell (current + direction). Check if the target cell is valid/walkable. If valid, update the cell position and set transform.position = grid.GetCellCenterWorld(cellPosition) to move the player to the center of the new cell.
What are some common uses for grid systems beyond level layout?
Grids are used for pathfinding graphs, fog of war systems, influence maps for AI, resource distribution in strategy games, building placement validation, turn-based combat positioning, inventory systems with spatial management, and procedural generation of levels with guaranteed connectivity.
How do I optimize pathfinding performance on large grids?
Use diagonal movement restrictions to reduce neighbors checked from 8 to 4, implement path caching for frequently used routes, use spatial partitioning to only pathfind in relevant grid sections, consider Jump Point Search for uniform-cost grids, and run pathfinding on background threads to avoid frame drops.