Search Unity

How to determine if a given GameObject is a prefab at runtime?

Discussion in 'Scripting' started by JesseSTG, Jan 20, 2020.

  1. JesseSTG

    JesseSTG

    Joined:
    Jan 10, 2019
    Posts:
    236
    At runtime, I want to list all the prefabs that are included within my game's build (for debugging reasons). I don't need to know about
    GameObject
    s that are instantiations of prefabs, or that are scene objects. I don't even want to list them. My question: given a reference to a GameObject at runtime, how can I determine if it is a prefab (not a prefab instance)?
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,742
    Generally this is accomplished by putting references to all of the prefabs into a list so you know that's what they are.

    Alternately you can do it by putting them all in a specifically-named folder under a folder named
    Resources
    and loading them with
    Resources.LoadAll<GameObject>()
    .

    My personal way of organizing collections of related GameObjects is to use a ScriptableObject approach, a script like this one. It has built up a lot of extra features over time but you can strip them out and use whatever works for you:

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. #if UNITY_EDITOR
    5. using UnityEditor;
    6. #endif
    7.  
    8. [CreateAssetMenu(menuName = "Collections/GameObjectCollection")]
    9. public class GameObjectCollection : ScriptableObject
    10. {
    11.     public string Description;
    12.  
    13.     public GameObject[] GameObjects;
    14.  
    15.     public GameObject this[int index]
    16.     {
    17.         get
    18.         {
    19.             return GameObjects[index];
    20.         }
    21.         set
    22.         {
    23.             GameObjects[index] = value;
    24.         }
    25.     }
    26.  
    27.     public    float    MasterScaling = 1.0f;
    28.  
    29.     public void InstantiateAll( Transform parent = null)
    30.     {
    31.         foreach( var go in GameObjects)
    32.         {
    33.             GameObject go2 = Instantiate<GameObject>( go);
    34.             go2.transform.SetParent( parent);
    35.         }
    36.     }
    37.  
    38.     public GameObject PickRandom()
    39.     {
    40.         return GameObjects[ Random.Range ( 0, GameObjects.Length)];
    41.     }
    42.  
    43.     int DeckPointer;
    44.     GameObject[] GameObjectDeck;
    45.  
    46.     void TryShuffle()
    47.     {
    48.         if (GameObjectDeck == null ||
    49.            (GameObjectDeck.Length != GameObjects.Length) ||
    50.            DeckPointer >= GameObjectDeck.Length)
    51.         {
    52.             DeckPointer = 0;
    53.  
    54.             GameObjectDeck = new GameObject[ GameObjects.Length];
    55.  
    56.             for (int i = 0; i < GameObjects.Length; i++)
    57.             {
    58.                 GameObjectDeck[i] = GameObjects[i];
    59.             }
    60.  
    61.             for (int i = 0; i < GameObjectDeck.Length - 1; i++)
    62.             {
    63.                 int j = Random.Range ( i, GameObjectDeck.Length);
    64.  
    65.                 var t = GameObjectDeck[i];
    66.                 GameObjectDeck[i] = GameObjectDeck[j];
    67.                 GameObjectDeck[j] = t;
    68.             }
    69.         }
    70.     }
    71.  
    72.     public GameObject PickNextShuffled()
    73.     {
    74.         TryShuffle ();
    75.  
    76.         GameObject result = GameObjectDeck [DeckPointer];
    77.  
    78.         DeckPointer++;
    79.  
    80.         return result;
    81.     }
    82.  
    83.     public int Length
    84.     {
    85.         get
    86.         {
    87.             if (GameObjects == null)
    88.             {
    89.                 return 0;
    90.             }
    91.             return GameObjects.Length;
    92.         }
    93.     }
    94.  
    95.     public int ConstrainIndex( int index, bool wrap = false)
    96.     {
    97.         if (GameObjects.Length < 1)
    98.         {
    99.             throw new System.IndexOutOfRangeException("GameObjectCollection.ConstrainIndex(): no GameObjects in " + name);
    100.         }
    101.  
    102.         if (index < 0) return 0;
    103.         if (index >= GameObjects.Length)
    104.         {
    105.             if (wrap)
    106.             {
    107.                 index = 0;
    108.             }
    109.             else
    110.             {
    111.                 index = GameObjects.Length - 1;
    112.             }
    113.         }
    114.         return index;
    115.     }
    116. }
    117.  
    118. #if UNITY_EDITOR
    119. [CustomEditor(typeof(GameObjectCollection))]
    120. public class GameObjectCollectionInspector : Editor
    121. {
    122.     Transform parent;
    123.  
    124.     void MakeContents()
    125.     {
    126.         GameObjectCollection goc = (GameObjectCollection)this.target;
    127.  
    128.         parent = new GameObject( "GOC:" + goc.name).transform;
    129.  
    130.         float spacing = 5.0f;
    131.         if (goc.MasterScaling >= 1.5f)
    132.         {
    133.             spacing = goc.MasterScaling * 1.1f;
    134.         }
    135.  
    136.         for (int i = 0; i < goc.GameObjects.Length; i++)
    137.         {
    138.             var go = goc.GameObjects[i];
    139.  
    140.             if (go)
    141.             {
    142.                 var o = (GameObject)PrefabUtility.InstantiatePrefab(go);
    143.                 if (o)
    144.                 {
    145.                     o.transform.SetParent( parent);
    146.  
    147.                     Vector3 pos = Vector3.right * (i - (goc.GameObjects.Length - 1) * 0.5f) * spacing;
    148.                     o.transform.localPosition = pos;
    149.                 }
    150.             }
    151.         }
    152.     }
    153.  
    154.     public override void OnInspectorGUI()
    155.     {
    156.         this.DrawDefaultInspector ();
    157.  
    158.         GUILayout.Space (25);
    159.  
    160.         GUILayout.BeginHorizontal();
    161.         if (parent)
    162.         {
    163.             GUI.color = new Color( 1.0f, 0.5f, 0.5f);
    164.             if(GUILayout.Button("Clear Instantiated Prefabs"))
    165.             {
    166.                 DestroyImmediate( parent.gameObject);
    167.                 parent = null;
    168.             }
    169.         }
    170.         else
    171.         {
    172.             GUI.color = new Color( 0.5f, 1.0f, 0.5f);
    173.             if(GUILayout.Button("Instantiate Prefabs Above"))
    174.             {
    175.                 MakeContents();
    176.             }
    177.         }
    178.         GUILayout.EndHorizontal();
    179.     }
    180. }
    181. #endif
    I have similar constructs for Texture2D and Materials and other collections of collections!
     
  3. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
    Joe-Censored likes this.
  4. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    You could try testing the .scene property of your GameObject.
    Code (CSharp):
    1. var inScene = gameObject.scene.isLoaded;
     
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Hmmm, I've never needed this. Whenever I use prefabs I either have a list of prefabs which I access by an enum pointing to the prefab's index in the list, or I have a public field ending with "Prefab". Kind of like below.

    Code (csharp):
    1. public GameObject CannonballPrefab;
    2.  
    3. public List<GameObject> ShipPrefabs;
    4. public enum ShipType { ShipOfTheLine, Frigate, Corvette, Cog };
    5.  
    6. public GameObject InstantiateShip (ShipType shipToInstantiate)
    7. {
    8.     return Instantiate(ShipPrefabs[(int)shipToInstantiate]);
    9. }
    So it is pretty easy to differentiate between prefabs and instances for me.
     
  6. JesseSTG

    JesseSTG

    Joined:
    Jan 10, 2019
    Posts:
    236
    Nope, can't do this. The reason I want to list all prefabs at runtime is that some builds of my game (different platforms, debugging configurations, demo vs. release builds, etc.) should entirely exclude certain prefabs. I want to check at runtime so I can be sure. If I add all my prefabs to a list in advance, that defeats the purpose.

    Can't do this either, as then I'd wind up loading all of my prefabs at once even in release builds. Yes, I could unload them manually as needed, but why bother?

    I'll try this out and report back, thank you.
     
  7. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,998
  8. JesseSTG

    JesseSTG

    Joined:
    Jan 10, 2019
    Posts:
    236
    This is what I wound up going with, using Owen-Reynolds' and grizzly's advice as a basis:

    Code (CSharp):
    1. // This code snippet is public domain, use as you'd like
    2. using System;
    3. using UnityEngine;
    4.  
    5. namespace CorundumGames.Utils
    6. {
    7.     public static class GameObjectExtensions
    8.     {
    9.         public static bool IsPrefab(this GameObject gameObject)
    10.         {
    11.             if (gameObject == null)
    12.             {
    13.                 throw new ArgumentNullException(nameof(gameObject));
    14.             }
    15.  
    16.             return
    17.                 !gameObject.scene.IsValid() &&
    18.                 !gameObject.scene.isLoaded &&
    19.                 gameObject.GetInstanceID() >= 0 &&
    20.                 // I noticed that ones with IDs under 0 were objects I didn't recognize
    21.                 !gameObject.hideFlags.HasFlag(HideFlags.HideInHierarchy
    22.                 // I don't care about GameObjects *inside* prefabs, just the overall prefab.
    23.                 ;
    24.         }
    25.     }
    26. }
    27.  
    It's probably not perfect, but it filters down the list of
    GameObject
    s well enough for me to manually inspect. The result list seems to change slightly based on which scene I have loaded, though. Any idea why that could be?
     
    radiatoryang likes this.
  9. grizzly

    grizzly

    Joined:
    Dec 5, 2012
    Posts:
    357
    Instance id's the likely cause of inconsistent results. It will change between Scene load and can be either positive or negative.

    I'd also urge caution using HideFlags.HideInHierarchy which can be set through code, and although you may not set it, something else somewhere down the line just might.

    I did test the .scene property before submitting my post (hence I missed Owen's post) but have vague memories of exploring this in the past without issue. I've not tested in build however, so something to explore.