Search Unity

FindObjectsOfType<> is broken when invoked from inside PrefabStage (nested prefabs)?

Discussion in 'Prefabs' started by a436t4ataf, May 24, 2019.

  1. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Is it just me? When I invoke this while viewing a prefabstage, I get returned results from a random scene in the project (not even the last-opened scene!). Seems to be returning objects from the first scene it finds in the project window?

    Also ... anyone know any other ways to find all components of type X, but that actually work with the new nested prefabs system?
     
  2. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    FindObjectsOfType is a runtime API and runtime APIs can't return objects from Prefab stages since it could break your game logic. The API can't know if it's called from a main scene context or from a Prefab stage.

    Instead you can use
    StageUtility.GetCurrentStageHandle().FindComponentsOfType<T>()
     
  3. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Excellent, thanks! Looks like that's exactly what I needed.

    Can I suggest:

    1. modify the FindComponentsOfType method to report an error if invoked while in a Prefab stage (since it's right now literally giving incorrect results, it shouldn't be allowed to execute)
    2. modify the docs for FCOT to explicitly say that there's a differnet method you should use in prefabstages!

    Docs here: https://docs.unity3d.com/ScriptReference/Object.FindObjectsOfType.html - don't mention any of this. There's no warning about the (current) incorrect results, and there's no advice on the existence of the StageUtility method.
     
  4. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,892
    Great!

    Can't do that. It's completely supported that your game code (in Play Mode for example) calls that method while you're also in Prefab Mode working on some Prefab.

    Agreed, we should do that.
     
    CarreraSilvio likes this.
  5. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    I've been thinking about this.

    I am executing code within the callback for "PrefabStage.prefabStageOpened" -- at which point, you can guarantee that FindComponentsOfType is going to generate corrupt data. I believe this is a clear situation where it's correct and safe to block that method call (preferably with an error message explaining why!).

    I see your point that invocation in general could happen while a game is running, so you cannot globally disable all calls to the method just because a prefab pane is open "somewhere" in one of the many EditorWindow instances.
     
  6. CarreraSilvio

    CarreraSilvio

    Joined:
    Jul 21, 2012
    Posts:
    31
    I was having the same issue when building a custom window to be used with prefab edit mode. Never heard of this StageUtility but it worked like a charm for me.
     
  7. daicoden

    daicoden

    Joined:
    Mar 21, 2022
    Posts:
    6
    Any chance of getting an `Object FindObjectOfType(System.Type type)` method signature version?

    For more context... I'm creating a dropdown which lists all the components that match an interface. I have to first use FindObjetsOfType - then search for those objects in the scene using the stage handle... It works but if there was a better reflective method available this would be cleaner.

    Code (CSharp):
    1.        
    2.         private IEnumerable GetInstances(InspectorProperty inspectorProperty)
    3.         {
    4.             Type baseClass = inspectorProperty.ValueEntry.BaseValueType;
    5.             return AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
    6.                     .Where(x => !x.IsAbstract)
    7.                     .Where(x => baseClass.IsAssignableFrom(x))
    8.                     .Where(x => typeof(Component).IsAssignableFrom(x))
    9.                     .SelectMany(Object.FindObjectsOfType)
    10.                     .OfType<Component>()
    11.                     .SelectMany(ComponentsInScene)
    12.                     .Where(x => baseClass.IsInstanceOfType(x))
    13.                     .Select(x => new ValueDropdownItem<T>(ComponentDisplayName(x), x as T)
    14.                     )
    15.                 ;
    16.         }
    17.  
    18.         private IEnumerable<T> ComponentsInScene<T>(T _) where T : Component
    19.         {
    20.             return StageUtility.GetCurrentStageHandle().FindComponentsOfType<T>();
    21.         }
    22.  
     
    Last edited: Oct 13, 2022