Skip to content

fix: gameobject-find can't locate objects in DontDestroyOnLoad or non-active scenes (#826) - #858

Open
gfranklin1 wants to merge 6 commits into
IvanMurzak:mainfrom
gfranklin1:fix/gameobject-find-dontdestroyonload
Open

fix: gameobject-find can't locate objects in DontDestroyOnLoad or non-active scenes (#826)#858
gfranklin1 wants to merge 6 commits into
IvanMurzak:mainfrom
gfranklin1:fix/gameobject-find-dontdestroyonload

Conversation

@gfranklin1

@gfranklin1 gfranklin1 commented Jul 5, 2026

Copy link
Copy Markdown

Fixes #826.

Root cause

GameObjectUtils.FindRootGameObjects() (called by gameobject-find when no explicit scene is given) only searched EditorSceneManager.GetActiveScene().GetRootGameObjects(). Two consequences:

  • Objects in an additively-loaded scene that isn't the active scene were silently missed.
  • Objects moved into DontDestroyOnLoad were always missed - that scene isn't part of SceneManager's enumerable scene list at all, and only exists once the Editor/Player is in Play Mode.

Fix

Two-part change to FindRootGameObjects(), mirrored across both Editor-gated variants of GameObjectUtils (GameObjectUtils.Editor.cs for Unity 6000.5+, GameObjectUtils.Editor.pre-Unity.6.5.cs for pre-6.5 - only the latter actually compiles on Unity 2022.3, which is why both needed the identical fix):

  1. Union root GameObjects from every opened scene via the existing SceneUtils.GetAllOpenedScenes(), not just the active one.
  2. When Application.isPlaying, additionally scan for DontDestroyOnLoad root objects via FindObjectsByType<GameObject>(FindObjectsInactive.Include, FindObjectsSortMode.None), filtered to parent == null && scene.name == "DontDestroyOnLoad".

No dedupe is needed: every GameObject belongs to exactly one scene, and DontDestroyOnLoad is disjoint from the SceneManager-enumerable set, so the two result sets can't overlap.

Prefab-stage handling and the explicit-scene-argument path are untouched. GameObjectData's result shape is untouched - the fix is entirely below that layer.

Why not GameObject.Find(name)

GameObject.Find only searches active GameObjects in loaded scenes and (in most Unity versions) still misses DontDestroyOnLoad root queries the same way - it wouldn't fix the actual bug, and it silently skips inactive objects, which would be a regression against the current recursive/inactive-aware search.

Testing

Verified with a real red→green cycle, not just written-and-assumed:

  • Tests/Editor/TestGameObjectUtils.cs::FindByName_AdditiveScene_ObjectIsFound - creates an additive, non-active EditMode scene; confirmed fails on unfixed code, passes with the fix.
  • Tests/Runtime/TestGameObjectUtils.DontDestroyOnLoad.cs::FindByName_DontDestroyOnLoad_ObjectIsFound (genuine PlayMode test) - confirmed fails on unfixed code, passes with the fix.
  • Full EditMode + PlayMode suites run clean afterward.

Out of scope

A related bug in GameObject.Hierarchy.cs:68 was noticed while investigating but is intentionally not touched here - flagging for maintainer input on whether it should be a separate PR.


Note: #836 is an existing open PR attempting the same issue. This PR is scoped narrower (DDOL + additive-scene fix only, no unrelated log changes) and fixes both halves of #826 - #836 only addresses the DDOL case, not the non-active-scene case.

…ve scenes (IvanMurzak#826)

FindRootGameObjects() only searched EditorSceneManager.GetActiveScene(),
so gameobject-find silently missed objects in additively-loaded but
non-active scenes, and always missed DontDestroyOnLoad (which isn't
part of SceneManager's scene list at all).

Now unions roots from every opened scene (SceneUtils.GetAllOpenedScenes),
plus a Play-Mode-only scan for DontDestroyOnLoad root objects. Mirrored
across both the Unity 6000.5+ and pre-6.5 Editor variants of
GameObjectUtils, since only the pre-6.5 file actually compiles on this
project's Unity version.
Copilot AI review requested due to automatic review settings July 5, 2026 20:26
@gfranklin1
gfranklin1 requested a review from IvanMurzak as a code owner July 5, 2026 20:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes gameobject-find missing GameObjects that live in additively-loaded (but non-active) scenes and in the DontDestroyOnLoad scene while in Play Mode, by widening the root search scope in the Editor-only GameObjectUtils.FindRootGameObjects() implementations.

Changes:

  • Update Editor FindRootGameObjects() to union roots from all opened scenes (not just the active scene) and additionally include DontDestroyOnLoad roots while playing.
  • Add an EditMode test covering loaded-but-not-active additive scenes.
  • Add a PlayMode test covering objects moved into DontDestroyOnLoad.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Utils/GameObjectUtils.Editor.cs Expands Editor root-object enumeration to include all opened scenes + DontDestroyOnLoad roots in Play Mode (Unity 6000.5+ branch).
Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Utils/GameObjectUtils.Editor.pre-Unity.6.5.cs Same root-object enumeration expansion for pre-6.5 Editor branch (Unity 2022/2023 compatible).
Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Tests/Editor/TestGameObjectUtils.cs Adds EditMode regression test for non-active additive scene lookup.
Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Tests/Runtime/TestGameObjectUtils.DontDestroyOnLoad.cs Adds PlayMode regression test for DontDestroyOnLoad lookup.
Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Tests/Runtime/TestGameObjectUtils.DontDestroyOnLoad.cs.meta Adds Unity meta for the new runtime test asset.
Files not reviewed (1)
  • Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Tests/Runtime/TestGameObjectUtils.DontDestroyOnLoad.cs.meta: Generated file

Assert EditorSceneManager.SaveScene succeeded instead of ignoring its
return value, and swap in a fresh untitled scene before deleting the
temp scene asset so the active scene never points at a deleted file.
@gfranklin1

Copy link
Copy Markdown
Author

Pushed a follow-up commit addressing both Copilot review comments on the additive-scene test's cleanup logic.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Tests/Runtime/TestGameObjectUtils.DontDestroyOnLoad.cs.meta: Generated file

FindRootGameObjects(scene: null) is Editor-only by design (see
GameObjectUtils.Runtime*.cs); a standalone Player build of this test
assembly always gets null there, unrelated to whether the DDOL fix
itself works. CI runs a standalone PlayMode job against this package
(Unity-Tests/*/Packages/manifest.json references it via testables), so
without this gate the test would fail there for the wrong reason.
IvanMurzak flagged FindObjectsByType/FindObjectsOfType as a
performance problem on heavy scenes, since it was scanning every
GameObject on every no-scene find call while playing. Copilot's
suggested alternative (SceneManager.GetSceneByName("DontDestroyOnLoad"))
was tried and confirmed not to work: that scene was never registered
in SceneManager's scene list to begin with, which is the entire reason
FindRootGameObjects() needed a workaround in the first place.

Instead, GetDontDestroyOnLoadScene() probes for the DDOL Scene handle
once via a throwaway GameObject, caches it, and self-heals via
IsValid() if it's ever invalidated (e.g. after a domain-reload-free
Play Mode restart). Every subsequent call is just a cheap
scene.GetRootGameObjects() instead of a world scan.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 5 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Tests/Runtime/TestGameObjectUtils.DontDestroyOnLoad.cs.meta: Generated file

Comment on lines +67 to +70
var probe = new UnityEngine.GameObject("~DDOLSceneProbe") { hideFlags = UnityEngine.HideFlags.HideAndDontSave };
UnityEngine.Object.DontDestroyOnLoad(probe);
var scene = probe.scene;
UnityEngine.Object.DestroyImmediate(probe);
Comment on lines +68 to +71
var probe = new GameObject("~DDOLSceneProbe") { hideFlags = HideFlags.HideAndDontSave };
Object.DontDestroyOnLoad(probe);
var scene = probe.scene;
Object.DestroyImmediate(probe);
Object.Destroy(probe) is deferred to end-of-frame, so the very next
GetRootGameObjects() call on the same frame could still see the probe
as a root. Filter it out by reference instead of relying on
DestroyImmediate's synchronous removal, since DestroyImmediate is
discouraged in Play Mode and GetDontDestroyOnLoadScene() only ever
runs while Application.isPlaying is true.
@gfranklin1

Copy link
Copy Markdown
Author

Side note about Copilot's latest critiques;
GetDontDestroyOnLoadScene() is only ever called from inside if (Application.isPlaying) in FindRootGameObjects(), so Application.isPlaying is always true here. An else DestroyImmediate branch would be dead code. Went with always Object.Destroy instead, plus filtering the probe out of the returned roots by reference so correctness doesn't depend on the destroy having completed yet (see the comment above the filter loop).

@gfranklin1

Copy link
Copy Markdown
Author

Hey @IvanMurzak, following up on this whenever you get a chance. I addressed the review feedback (DestroyImmediate concern in GetDontDestroyOnLoadScene(), now uses Object.Destroy + reference-filtering to avoid dead code from the isPlaying branch) and the additive-scene + DDOL tests are both green. Let me know if anything else needs changing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gameobject-find can't locate GameObjects in DontDestroyOnLoad scene

3 participants