Quick answer: PCG graphs produce non-deterministic results because random nodes use unseeded or time-based seeds, graph branches execute in unpredictable order, or floating-point precision varies across platforms. Set explicit seeds derived from the PCG component, enforce execution order between branches, and avoid async or frame-dependent operations.
Here is how to fix Unreal PCG determinism when you get different results across runs. You build a procedural generation graph that scatters trees, rocks, and grass across your landscape. It looks perfect. You close the editor, reopen the level, and the placement is completely different. Same graph, same settings, different output. Or worse — it looks the same on your machine but your teammate gets an entirely different arrangement from the same project files. The Procedural Content Generation framework is doing exactly what you asked, but random operations without fixed seeds produce different results every time they run.
The Symptom
A PCG graph that uses any form of randomness — random point distribution, random selection, density filtering with random thresholds, random rotation or scale — generates different results on each execution. This manifests in several ways:
Same machine, different runs. You generate, close the editor, reopen, regenerate, and get different placement. Trees are in different positions, the density varies, selected meshes change. If you generate twice in the same editor session without closing, the results may also differ.
Different machines, same project. You and a teammate open the same level from source control and generate PCG. Your results differ. This causes merge conflicts if the generated data is saved to the level, and visual inconsistencies if generation happens at runtime.
Editor vs. runtime. The PCG output in the editor (after generating in-editor) does not match the runtime output when the same graph executes during gameplay. This is particularly problematic for games that generate worlds at load time and expect consistent results from a given world seed.
What Causes This
Unseeded random nodes. PCG nodes that involve randomness (point samplers, random filters, random attribute setters) use a seed to initialize their random number generators. If the seed mode is left at the default, it may derive from the system time or an internal counter that changes between executions. Each run gets a different seed, producing different random sequences and therefore different output. This is the most common cause and the easiest to fix.
Non-deterministic execution order. PCG graphs can have parallel branches that are not explicitly ordered relative to each other. When two branches feed into the same downstream node, the order in which they execute can vary between runs. If the downstream node processes inputs sequentially (e.g., a union or merge), the input order affects the output order, which affects any subsequent random operations that depend on point ordering.
Async execution and task scheduling. Some PCG operations can run asynchronously across multiple threads. Thread scheduling is inherently non-deterministic — the OS decides which thread runs when. If async tasks produce results that are combined in completion order rather than a fixed order, the final output varies between runs. This is more common in complex graphs with heavy computation nodes.
Floating-point non-determinism. Different CPU architectures, compiler optimizations, and even SIMD instruction sets can produce slightly different floating-point results for the same operations. In PCG, a tiny difference in a distance calculation can push a point just above or below a density threshold, changing whether it is included or excluded. These differences cascade through the graph, and after several stages of filtering and transformation, the output can be completely different.
The Fix
Step 1: Set deterministic seeds on all random nodes. Open your PCG graph and select every node that has a seed setting. Change the seed mode to From Component so it derives from the PCG component’s seed property, which you control explicitly. For nodes that need unique seeds, use the Seed From Position option, which generates a seed based on the node’s position in the graph — this is consistent across runs as long as the graph structure does not change.
// Setting the PCG component seed in C++
void AMyWorldGenerator::InitializePCG(int32 WorldSeed)
{
UPCGComponent* PCGComp =
FindComponentByClass<UPCGComponent>();
if (PCGComp)
{
// Set a fixed seed for deterministic output
PCGComp->Seed = WorldSeed;
// Force synchronous generation for determinism
PCGComp->GenerationTrigger =
EPCGComponentGenerationTrigger::GenerateAtRuntime;
// Generate with the fixed seed
PCGComp->Generate();
UE_LOG(LogTemp, Log,
TEXT("PCG generated with seed: %d"), WorldSeed);
}
}
The component seed is the root of the seed hierarchy. Every node in the graph that uses From Component seed mode derives its local seed from this value combined with its node index. Change the component seed and you get a different but equally deterministic output. Same component seed always produces the same result.
Step 2: Enforce execution order. Identify branches in your graph that execute in parallel and feed into shared downstream nodes. Add explicit execution dependencies between them by connecting their execution pins in a defined order. If two branches produce point sets that are merged, connect them sequentially so Branch A always completes before Branch B starts.
// In a custom PCG node, force deterministic point ordering
void UMyPCGMergeNode::ExecuteInternal(
FPCGContext* Context) const
{
TArray<FPCGPoint> MergedPoints;
// Sort inputs by a stable key before merging
TArray<FPCGTaggedData> SortedInputs = Context->InputData;
SortedInputs.Sort([](const FPCGTaggedData& A,
const FPCGTaggedData& B)
{
return A.Pin < B.Pin; // Stable sort by pin name
});
for (const FPCGTaggedData& Input : SortedInputs)
{
// Append points in deterministic order
const UPCGPointData* PointData =
Cast<UPCGPointData>(Input.Data);
if (PointData)
{
MergedPoints.Append(PointData->GetPoints());
}
}
}
The key principle is that point ordering must be deterministic before any random operation. If points arrive in a different order, even with the same seed, random operations applied per-point will map different random values to different spatial positions, changing the output.
Handling Floating-Point Determinism
Cross-platform floating-point determinism is a deep problem that no engine fully solves. For PCG specifically, the practical approach is to avoid relying on tight floating-point thresholds. If your density filter removes points where value < 0.5, a point at 0.499999 on one platform and 0.500001 on another will produce different results. Widen your thresholds or quantize values to fixed precision before comparison.
For distance-based operations (scatter points at least N units apart), use integer grid snapping rather than continuous floating-point distances. Snap candidate positions to a grid, compute distances on the grid, and then apply a small random offset after the inclusion decision is made. The inclusion decision is now integer-based and fully deterministic; only the visual jitter varies, and that can be seeded separately.
If your game requires bit-exact determinism across platforms (e.g., for competitive multiplayer with procedural maps), consider running PCG generation on a server and replicating the results to clients rather than generating independently on each machine.
Async Execution Pitfalls
PCG supports generating in async mode to avoid hitching the game thread. However, async generation can introduce non-determinism because task completion order is not guaranteed. If your graph has nodes that merge results from multiple async tasks, the merge order depends on which task finishes first, which depends on the thread scheduler.
The safest approach is to use synchronous generation for any graph that requires determinism. Set the PCG component’s generation mode to synchronous and accept the potential frame hitch during generation. If the hitch is unacceptable, generate during a loading screen or spread generation across multiple frames using a coroutine-style approach where each frame processes a fixed portion of the graph in a deterministic order.
“A procedural system without fixed seeds is a random system. If you cannot reproduce the output from the input, you cannot debug it, you cannot test it, and you cannot ship it. Seed everything.”
Related Issues
If your PCG graph is deterministic but produces visible artifacts like grid patterns or clumping, see PCG Visible Grid Pattern in Scatter for Poisson disk sampling and jitter techniques. If PCG generation causes hitches during gameplay, check PCG Runtime Generation Hitching for async generation and level-of-detail approaches.
Set every PCG component seed from your world seed at load time — determinism starts at the root.