Build a Tower Defense Game using C++, Raylib & A* Pathfinding

Lesson 2 of 8

The Grid: Modeling the Map

A tower defense map is a grid: some cells are walkable ground, some are permanent walls, one is where enemies spawn, one is the goal they're marching toward. Model that before drawing a single pixel.

const int GRID_COLS = 20;
const int GRID_ROWS = 12;
const int CELL_SIZE = 40;

enum class CellType { Empty, Wall, Start, Goal };

CellType grid[GRID_ROWS][GRID_COLS];

struct GridPos {
    int row, col;
    bool operator==(const GridPos& other) const {
        return row == other.row && col == other.col;
    }
};

void initGrid() {
    for (int r = 0; r < GRID_ROWS; r++) {
        for (int c = 0; c < GRID_COLS; c++) {
            grid[r][c] = CellType::Empty;
        }
    }
    grid[5][0] = CellType::Start;
    grid[5][GRID_COLS - 1] = CellType::Goal;
}

GridPos bundles a row and column together, and operator== is defined explicitly because C++ doesn't generate one for you automatically, comparing two GridPos values by == later (which the pathfinding lessons do constantly) needs this to actually compile.

Drawing the grid

void drawGrid() {
    for (int r = 0; r < GRID_ROWS; r++) {
        for (int c = 0; c < GRID_COLS; c++) {
            int x = c * CELL_SIZE;
            int y = r * CELL_SIZE;

            Color color = LIGHTGRAY;
            if (grid[r][c] == CellType::Wall) color = DARKGRAY;
            if (grid[r][c] == CellType::Start) color = GREEN;
            if (grid[r][c] == CellType::Goal) color = RED;

            DrawRectangle(x, y, CELL_SIZE - 2, CELL_SIZE - 2, color);
        }
    }
}

Every cell is drawn as a CELL_SIZE - 2 square instead of a full CELL_SIZE square, leaving a thin 2-pixel gap so adjacent cells are visually distinct instead of forming one solid block.

Converting between grid coordinates and pixels

Two small helpers pay for themselves immediately, everything from here on needs to go back and forth between "which cell" and "where on screen":

Vector2 gridToPixel(GridPos pos) {
    return Vector2{
        pos.col * (float) CELL_SIZE + CELL_SIZE / 2.f,
        pos.row * (float) CELL_SIZE + CELL_SIZE / 2.f
    };
}

GridPos pixelToGrid(Vector2 pixel) {
    return GridPos{
        (int)(pixel.y / CELL_SIZE),
        (int)(pixel.x / CELL_SIZE)
    };
}

gridToPixel returns the center of a cell (adding half a CELL_SIZE), not its top-left corner, that's what you want for placing an enemy or tower sprite so it looks centered in its cell rather than jammed into a corner.

TIP

pixelToGrid is exactly what you'll feed GetMousePosition() into later, to turn "where the player clicked" into "which cell they clicked."

📝 Grid Modeling Quiz

Passing score: 70%
  1. 1.Why does GridPos define its own operator==?

  2. 2.gridToPixel returns the top-left corner of a cell, not its center.

  3. 3.pixelToGrid is designed to convert the result of GetMouse____() into a grid cell.