Search Unity

Can you tell if a GameoOject is a prefab in play mode?

Discussion in 'Prefabs' started by LittleDreamerGames, Jan 26, 2020.

  1. LittleDreamerGames

    LittleDreamerGames

    Joined:
    Apr 9, 2017
    Posts:
    72
    I've been trying to call PrefabUtility.GetPrefabAssetType from inside a MonoBehaviour's Start function but it always returns NotAPrefab even though the MonoBehaviour is attached to a prefab. Is there even a way to detect if a GameObject is a prefab in play mode with a MonoBehaviour? Funny thing is that It does work in an Editor script in play mode.

    The attachment is a prefab made with Unity 2019.2.19f1.

    Run the sample scene and check out the console. The information printed says that the JustSomePrefab is not a prefab and is not part of any prefab. Lies :).

    I also created a PrefabInfo script that will show prefab info. It displays the correct info when attached to the prefab in the scene.

    Is this a bug, or as designed? If it's as designed, can I request a feature to detect if a game object is a prefab in play mode from a MonoBehaviour?
     

    Attached Files:

  2. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    PrefabUtility only works in the Editor. A similar question with solutions can be found here.
     
  3. LittleDreamerGames

    LittleDreamerGames

    Joined:
    Apr 9, 2017
    Posts:
    72
    Thanks for the response grizzly! None of those solutions are working for me. I even tried the "gameObject.scene.name" one.

    To be clear, I only need this to work while running the game from inside the Editor. I don't need to use it in a build.

    PrefabUtility works when you use it inside a class that inherits from Editor, but it does not work inside a class that inherits from MonoBehaviour.

    The reason this situation is kind of odd is that the legacy PrefabUtility.GetPrefabType does return the correct information when you use it in a class that inherits from MonoBehaviour. It's the new prefab methods that don't work like I'd expect. This leads me to believe that this was just an oversight.

    Edited: I take that back. It doesn't work with PrefabUtility.GetPrefabType either.
     
    Last edited: Jan 26, 2020
  4. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    Ah, it was to my understanding that you were testing against an attached Prefab, not an instance of a Prefab - which is quite different :)
    This doesn't work for me. PrefabInfo also returns NotAPrefab during Playmode.

    PrefabUtility is dependant on Editor context (like the Hierarchy Window) which is absent for Playmode.

    To work around this limitation you could capture the Edit-time result in a serialized field and test that at runtime instead.

    For example:
    Code (CSharp):
    1. public class PrefabInstanceDebug : MonoBehaviour
    2. {
    3.     public bool IsPrefab;
    4.  
    5.     private void Start ()
    6.     {
    7.         Debug.Log("**************** Instance ****************");
    8.         Debug.Log("IsPrefab: " + IsPrefab, gameObject);
    9.     }
    10.  
    11.     #if UNITY_EDITOR
    12.  
    13.     private void OnValidate ()
    14.     {
    15.         if (!Application.isPlaying)
    16.         {
    17.             IsPrefab = PrefabUtility.IsPartOfAnyPrefab(gameObject);
    18.         }
    19.     }
    20.  
    21.     #endif
    22. }
     
    LittleDreamerGames likes this.
  5. LittleDreamerGames

    LittleDreamerGames

    Joined:
    Apr 9, 2017
    Posts:
    72
    So close, and I was so hopeful! It kind of does the same thing that PrefabUtility does. When you're not in play mode it sets the IsPrefab correctly. But as soon as it runs, the class is reallocated and IsPrefab automatically retains the default value because it'll never reach the IsPartOfAnyPrefab line.
     
  6. LittleDreamerGames

    LittleDreamerGames

    Joined:
    Apr 9, 2017
    Posts:
    72
    lol, doh! Your example uses a public variable which automatically serializes. I made mine a private property so nothing can set it except the class it lives in. I just needed to add the [SerializeField] attribute and now it works :D

    Thanks for your help! I wasn't even aware of the OnValidate method, and that was the ticket!
     
    grizzly likes this.
  7. LittleDreamerGames

    LittleDreamerGames

    Joined:
    Apr 9, 2017
    Posts:
    72
    For reference:
    Code (CSharp):
    1.  
    2. public class SerializeableObject : MonoBehaviour
    3. {
    4. #if UNITY_EDITOR
    5.     [SerializeField]
    6.     private bool _isPrefab = false;
    7.     public bool isPrefab { get{ return _isPrefab; } }
    8.  
    9.     private void OnValidate()
    10.     {
    11.         if (!Application.isPlaying)
    12.         {
    13. #if UNITY_2018_2_OR_NEWER
    14.             _isPrefab = (PrefabUtility.GetPrefabAssetType(gameObject) != PrefabAssetType.NotAPrefab);
    15. #else
    16.             _isPrefab = (PrefabUtility.GetPrefabType(gameObject) != PrefabType.None);
    17. #endif // UNITY_2018_2_OR_NEWER
    18.         }
    19.     }
    20. #endif // UNITY_EDITOR
    21. }
    I do it this way because I use...

    Resources.FindObjectsOfTypeAll<SerializeableObject>();


    ...to grab all the objects for processing, except in my case I don't want to process prefabs.