fix: gameobject-find can't locate objects in DontDestroyOnLoad or non-active scenes (#826) - #858
Conversation
…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.
There was a problem hiding this comment.
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 includeDontDestroyOnLoadroots 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.
|
Pushed a follow-up commit addressing both Copilot review comments on the additive-scene test's cleanup logic. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
| var probe = new UnityEngine.GameObject("~DDOLSceneProbe") { hideFlags = UnityEngine.HideFlags.HideAndDontSave }; | ||
| UnityEngine.Object.DontDestroyOnLoad(probe); | ||
| var scene = probe.scene; | ||
| UnityEngine.Object.DestroyImmediate(probe); |
| 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.
|
Side note about Copilot's latest critiques; |
|
Hey @IvanMurzak, following up on this whenever you get a chance. I addressed the review feedback ( |
Fixes #826.
Root cause
GameObjectUtils.FindRootGameObjects()(called bygameobject-findwhen no explicit scene is given) only searchedEditorSceneManager.GetActiveScene().GetRootGameObjects(). Two consequences:DontDestroyOnLoadwere always missed - that scene isn't part ofSceneManager'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 ofGameObjectUtils(GameObjectUtils.Editor.csfor Unity 6000.5+,GameObjectUtils.Editor.pre-Unity.6.5.csfor pre-6.5 - only the latter actually compiles on Unity 2022.3, which is why both needed the identical fix):SceneUtils.GetAllOpenedScenes(), not just the active one.Application.isPlaying, additionally scan forDontDestroyOnLoadroot objects viaFindObjectsByType<GameObject>(FindObjectsInactive.Include, FindObjectsSortMode.None), filtered toparent == 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.Findonly searches active GameObjects in loaded scenes and (in most Unity versions) still missesDontDestroyOnLoadroot 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.Out of scope
A related bug in
GameObject.Hierarchy.cs:68was 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.