R&D · UE5 · 3D Game · C++

3D Minesweeper

3D Minesweeper

Classic Minesweeper rebuilt in 3D with Unreal Engine 5 C++. The grid spawns as physical actors; cell reveals use line traces against the world. Bombs are placed via a shuffle-pool algorithm that guarantees no duplicates. The game-over reveal floods all cells in one pass.

UE5 C++ Line Trace Pool Shuffle Flood Reveal 3D Grid
What I Built
  • Duplicate-free bomb placement — builds an index pool [0..N), Fisher-Yates shuffles it, takes the first BombCount entries. Guaranteed unique, O(N) time.
  • Line-trace reveal — player click fires a line trace; hit actor is cast to AMinesweeperCell and reveals itself or triggers game over.
  • Flood-fill on zero-count cells — if a revealed cell has no adjacent bombs, it recursively reveals all non-bomb neighbours (BFS queue, visited set).
  • Game-over revealGameOver() iterates all cells and calls Reveal() on each, flipping materials to show bomb locations.
  • Cell actor design — each cell is a minimal AActor; state (hidden/revealed/flagged/bomb) drives material parameter collection swaps, no Blueprint logic.

Grid Init — Duplicate-Free Bomb Placement

Pool shuffle: all valid indices in a TArray, shuffled, first N taken as bomb positions. No hash-set needed, no retry loop.

MinesweeperGrid.cpp — InitializeGrid()
void AMinesweeperGrid::InitializeGrid()
{
    Cells.Empty();
    const int32 Total = GridWidth * GridHeight;

    // Spawn all cell actors on the grid
    for (int32 i = 0; i < Total; ++i)
    {
        FVector Loc = IndexToWorld(i);
        AMinesweeperCell* Cell = GetWorld()->SpawnActor<AMinesweeperCell>(CellClass, Loc, FRotator::ZeroRotator);
        Cells.Add(Cell);
    }

    // Fisher-Yates pool shuffle — duplicate-free bomb placement
    TArray<int32> Options;
    for (int32 i = 0; i < Total; ++i) Options.Add(i);
    for (int32 i = Total - 1; i > 0; --i)
        Options.Swap(i, FMath::RandRange(0, i));

    for (int32 k = 0; k < BombCount; ++k)
        Cells[Options[k]]->SetBomb(true);

    // Set adjacent-bomb counts for non-bomb cells
    for (int32 i = 0; i < Total; ++i)
        if (!Cells[i]->IsBomb())
            Cells[i]->SetAdjacentCount(CountAdjacentBombs(i));
}

void AMinesweeperGrid::LineTraceRevealCell(const FVector& Start, const FVector& End)
{
    FHitResult Hit;
    if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility))
    {
        if (AMinesweeperCell* Cell = Cast<AMinesweeperCell>(Hit.GetActor()))
        {
            if (Cell->IsBomb()) GameOver();
            else              Cell->Reveal();
        }
    }
}

void AMinesweeperGrid::GameOver()
{
    for (AMinesweeperCell* Cell : Cells) Cell->Reveal();  // expose all
    OnGameOver.Broadcast();
}
Engine Unreal Engine 5 Language C++ Bomb placement Fisher-Yates pool shuffle Reveal Line trace → BFS flood fill Category R&D · Puzzle Source github.com/khaled71612000 ↗
Connect