
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.
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.x + y * GridSizeX flat indexing keeps 8 neighbor lookups cache-friendly and avoids 2D array indirection overhead.GetAliveNeighbors() iterates all 8 offsets with bounds clamping; returns alive count used by all 4 Conway rules.
Writes next generation into NewStates, then swaps. The four Conway rules are expressed as a single integer comparison on the neighbour count.
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;
}