Build Breakout using C++ and SFML

Lesson 5 of 7

Bricks and Collisions

A Breakout level is just a grid of rectangles. Build it once at startup, then remove bricks as the ball destroys them.

Laying out the grid

#include <vector>

std::vector<sf::RectangleShape> createBricks(int rows, int cols) {
    std::vector<sf::RectangleShape> bricks;
    float brickWidth = 75.f;
    float brickHeight = 20.f;
    float padding = 5.f;

    for (int row = 0; row < rows; row++) {
        for (int col = 0; col < cols; col++) {
            sf::RectangleShape brick(sf::Vector2f(brickWidth, brickHeight));
            brick.setPosition(
                col * (brickWidth + padding) + 35.f,
                row * (brickHeight + padding) + 50.f
            );
            brick.setFillColor(sf::Color::Cyan);
            bricks.push_back(brick);
        }
    }
    return bricks;
}

Detecting collisions

Every drawable shape has getGlobalBounds(), an sf::FloatRect describing its bounding box in the window, and every sf::FloatRect has .intersects(other) to test overlap:

for (std::size_t i = 0; i < bricks.size(); i++) {
    if (ball.shape.getGlobalBounds().intersects(bricks[i].getGlobalBounds())) {
        ball.velocity.y = -ball.velocity.y; // simplified bounce
        bricks.erase(bricks.begin() + i);
        score += 10;
        break; // only handle one collision per frame
    }
}

The pieces

  • getGlobalBounds() returns the shape's bounding box after any position, rotation, or scale is applied, use this (not raw size) for collision checks.
  • .intersects(other) returns true if the two rectangles overlap at all.
  • bricks.erase(bricks.begin() + i) removes the destroyed brick from the vector, shifting later elements down. Once a collision is handled, break out of the loop, the indices are no longer valid after an erase.
  • Flipping velocity.y on every brick hit is a simplification, real Breakout clones check which side of the brick was hit to decide whether to flip x or y. That's a great stretch goal for the final project.

WARNING

Erasing from a std::vector while iterating it with a range-based for loop (for (auto& brick : bricks)) invalidates the iterator and causes undefined behavior. Use an index-based loop with break, like above, whenever the loop body might erase.

📝 Bricks & Collisions Quiz

Passing score: 70%
  1. 1.Which method checks whether two sf::FloatRect bounding boxes overlap?

  2. 2.It is safe to erase elements from a std::vector while iterating it with a range-based for loop.

  3. 3.The method that returns a shape's bounding box after position and scale are applied is get____Bounds().