"GameObject.Destroy keeps rendering in edit mode — deferred queue not processed between regenerations"
After regenerating a procedural world in edit mode, the scene tree shows two root GameObjects where there should be one — both enabled, each with hundreds of child ModelRenderers. Identical generation specs hide the problem (the two worlds overlap perfectly), but changing a parameter (amplitude, scale, sea level) makes the previous world render through the new one. A screenshot after generating at amplitude 80 then 10 shows tall mountains from the old world poking through the flat new terrain.
GameObject.Destroy() enqueues the target for deferred destruction — the actual teardown happens later in the frame lifecycle. In edit mode, the deferred destroy queue is not processed between MCP-driven regeneration steps. A regeneration workflow that does previousRoot?.Destroy() then immediately builds a new root leaves the previous root alive and rendering. The scene tree confirms two roots, both enabled, with identical child counts.
This is the same family of bug as any "static reference repointed mid-play" issue: the old object is still alive, still rendering, but the code has moved on to a new reference.
Replace Destroy() with DestroyImmediate() for edit-mode world teardown:
// Sweep ALL matching roots — not just the first — to also collect any previously leaked roots
foreach (var root in Scene.GetAllObjects(false)
.Where(go => go.Name == "world_root" && go.Parent == null))
{
root.DestroyImmediate();
}Key details:
DestroyImmediate()is synchronous and valid in both edit and play mode (the editor tools themselves use it for scene object deletes).- Sweep all matches, not just
FirstOrDefault()— this also cleans up any root that leaked from a previous failed regeneration.
As a regression net, add a diagnostic tool that returns the world root count alongside other generation metadata, asserted to equal 1 after every mutating step.
DestroyImmediate() bypasses the deferred queue entirely — the GameObject is torn down synchronously before the next line executes. This guarantees the old world is gone before the new one is built, regardless of whether the engine processes the deferred queue in the current frame phase. Sweeping all matching roots (not just the expected one) adds resilience against accumulated leaks from interrupted or failed prior regenerations.