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)?
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:
using UnityEngine;
using System.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif
[CreateAssetMenu(menuName = "Collections/GameObjectCollection")]
public class GameObjectCollection : ScriptableObject
{
public string Description;
public GameObject[] GameObjects;
public GameObject this[int index]
{
get
{
return GameObjects[index];
}
set
{
GameObjects[index] = value;
}
}
public float MasterScaling = 1.0f;
public void InstantiateAll( Transform parent = null)
{
foreach( var go in GameObjects)
{
GameObject go2 = Instantiate<GameObject>( go);
go2.transform.SetParent( parent);
}
}
public GameObject PickRandom()
{
return GameObjects[ Random.Range ( 0, GameObjects.Length)];
}
int DeckPointer;
GameObject[] GameObjectDeck;
void TryShuffle()
{
if (GameObjectDeck == null ||
(GameObjectDeck.Length != GameObjects.Length) ||
DeckPointer >= GameObjectDeck.Length)
{
DeckPointer = 0;
GameObjectDeck = new GameObject[ GameObjects.Length];
for (int i = 0; i < GameObjects.Length; i++)
{
GameObjectDeck[i] = GameObjects[i];
}
for (int i = 0; i < GameObjectDeck.Length - 1; i++)
{
int j = Random.Range ( i, GameObjectDeck.Length);
var t = GameObjectDeck[i];
GameObjectDeck[i] = GameObjectDeck[j];
GameObjectDeck[j] = t;
}
}
}
public GameObject PickNextShuffled()
{
TryShuffle ();
GameObject result = GameObjectDeck [DeckPointer];
DeckPointer++;
return result;
}
public int Length
{
get
{
if (GameObjects == null)
{
return 0;
}
return GameObjects.Length;
}
}
public int ConstrainIndex( int index, bool wrap = false)
{
if (GameObjects.Length < 1)
{
throw new System.IndexOutOfRangeException("GameObjectCollection.ConstrainIndex(): no GameObjects in " + name);
}
if (index < 0) return 0;
if (index >= GameObjects.Length)
{
if (wrap)
{
index = 0;
}
else
{
index = GameObjects.Length - 1;
}
}
return index;
}
}
#if UNITY_EDITOR
[CustomEditor(typeof(GameObjectCollection))]
public class GameObjectCollectionInspector : Editor
{
Transform parent;
void MakeContents()
{
GameObjectCollection goc = (GameObjectCollection)this.target;
parent = new GameObject( "GOC:" + goc.name).transform;
float spacing = 5.0f;
if (goc.MasterScaling >= 1.5f)
{
spacing = goc.MasterScaling * 1.1f;
}
for (int i = 0; i < goc.GameObjects.Length; i++)
{
var go = goc.GameObjects[i];
if (go)
{
var o = (GameObject)PrefabUtility.InstantiatePrefab(go);
if (o)
{
o.transform.SetParent( parent);
Vector3 pos = Vector3.right * (i - (goc.GameObjects.Length - 1) * 0.5f) * spacing;
o.transform.localPosition = pos;
}
}
}
}
public override void OnInspectorGUI()
{
this.DrawDefaultInspector ();
GUILayout.Space (25);
GUILayout.BeginHorizontal();
if (parent)
{
GUI.color = new Color( 1.0f, 0.5f, 0.5f);
if(GUILayout.Button("Clear Instantiated Prefabs"))
{
DestroyImmediate( parent.gameObject);
parent = null;
}
}
else
{
GUI.color = new Color( 0.5f, 1.0f, 0.5f);
if(GUILayout.Button("Instantiate Prefabs Above"))
{
MakeContents();
}
}
GUILayout.EndHorizontal();
}
}
#endif
I have similar constructs for Texture2D and Materials and other collections of collections!
Sure, generally you “know” something is a prefab since it’s in a variable which was created to point to a prefab. But How to know if a GameObject is a prefab? - Questions & Answers - Unity Discussions has several not-too-old suggestions for directly testing. One uses gameObject.scene, some others use PrefabUtility.
You could try testing the .scene
property of your GameObject.
var inScene = gameObject.scene.isLoaded;
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.
public GameObject CannonballPrefab;
public List<GameObject> ShipPrefabs;
public enum ShipType { ShipOfTheLine, Frigate, Corvette, Cog };
public GameObject InstantiateShip (ShipType shipToInstantiate)
{
return Instantiate(ShipPrefabs[(int)shipToInstantiate]);
}
So it is pretty easy to differentiate between prefabs and instances for me.
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.
How to know if a GameObject is a prefab? - Questions & Answers - Unity Discussions covers using “dot-scene”, along with a few other things.
This is what I wound up going with, using Owen-Reynolds’ and grizzly’s advice as a basis:
// This code snippet is public domain, use as you'd like
using System;
using UnityEngine;
namespace CorundumGames.Utils
{
public static class GameObjectExtensions
{
public static bool IsPrefab(this GameObject gameObject)
{
if (gameObject == null)
{
throw new ArgumentNullException(nameof(gameObject));
}
return
!gameObject.scene.IsValid() &&
!gameObject.scene.isLoaded &&
gameObject.GetInstanceID() >= 0 &&
// I noticed that ones with IDs under 0 were objects I didn't recognize
!gameObject.hideFlags.HasFlag(HideFlags.HideInHierarchy
// I don't care about GameObjects *inside* prefabs, just the overall prefab.
;
}
}
}
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?
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.