R&D · Cellular Automaton · UE5 · C++

Conway's Game of Life

Conway's Game of Life

Conway's Game of Life implemented in Unreal Engine 5 C++. A configurable grid of cells evolves each tick via the classic four rules. The simulation uses a dual TArray read/write pattern so every cell is evaluated on the same snapshot. Cells render as mesh instances — no per-cell AActor overhead.

Cellular Automaton Dual-Array Sim 1D Grid Indexing UE5 C++ ISM
What I Built
  • 2-array swap pattern — current state in CellStates, next state written to NewStates; 1 MoveTemp swap at step end. Ensures every cell is evaluated on the same snapshot with 0 mid-step corruption.
  • 1D-indexed 2D gridx + y * GridSizeX flat indexing keeps 8 neighbor lookups cache-friendly and avoids 2D array indirection overhead.
  • 8-neighbor queryGetAliveNeighbors() iterates all 8 offsets with bounds clamping; returns alive count used by all 4 Conway rules.
  • Configurable tick rate — 1 accumulator1 float accumulator drives a fixed-step loop; simulation speed is fully decoupled from frame rate with 0 drift.
  • ISM rendering — 1 draw call — alive cells add an ISM instance; dead cells are removed. 1 draw call renders the entire grid regardless of live cell count.

Simulation Step — UpdateGrid()

Writes next generation into NewStates, then swaps. The four Conway rules are expressed as a single integer comparison on the neighbour count.

ConwayGridManager.cpp — UpdateGrid()
void AConwayGridManager::UpdateGrid()
{
    TArray<bool> NewStates = CellStates;  // copy → write target

    for (int32 y = 0; y < GridSizeY; ++y)
    for (int32 x = 0; x < GridSizeX; ++x)
    {
        const int32 Idx   = x + y * GridSizeX;
        const int32 Alive = GetAliveNeighbors(x, y);
        const bool  bWas  = CellStates[Idx];

        // Conway rules: survive on 2-3 neighbours; birth on exactly 3
        NewStates[Idx] = bWas ? (Alive == 2 || Alive == 3)
                               : (Alive == 3);
    }

    CellStates = MoveTemp(NewStates);  // swap — zero alloc
    UpdateMeshInstances();
}

int32 AConwayGridManager::GetAliveNeighbors(int32 X, int32 Y) const
{
    int32 Count = 0;
    for (int32 dy = -1; dy <= 1; ++dy)
    for (int32 dx = -1; dx <= 1; ++dx)
    {
        if (dx == 0 && dy == 0) continue;  // skip self
        const int32 NX = X + dx, NY = Y + dy;
        if (NX < 0 || NX >= GridSizeX || NY < 0 || NY >= GridSizeY)
            continue;  // out of bounds
        // 1D index from 2D coords: x + y * width
        if (CellStates[NX + NY * GridSizeX]) ++Count;
    }
    return Count;
}
Engine Unreal Engine 5 Language C++ Pattern Dual TArray swap Indexing 1D flat — x + y * GridSizeX Category R&D · Simulation Source github.com/khaled71612000 ↗
Connect