Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Finding out if a property is a "live" game object or prefab

Discussion in 'Scripting' started by Korno, Dec 12, 2014.

  1. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    Hey guys,

    I have a script on a game object that references another game object via a public property set in the editor.

    The issue I have is, this property can either be assigned a "live" game object in the scene or a prefab. If it is a prefab reference then I want to instantiate an instance of the gameobject before using it. However, if it is an active game object then the instantiate stage is just skipped.

    As this is a script that is going to be used by other developers, I can't control how people will use it and I have been asked to provide both support.

    Is there an easy way to see if a game object reference is a prefab or a "live" game object?
     
  2. Zaladur

    Zaladur

    Joined:
    Oct 20, 2012
    Posts:
    392
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    Only problem is that PrefabUtility is only available at editor time, and not at runtime, as it's part of the UnityEditor namespace.

    What you can do is write a custom editor for the script, have a second bool property, and then test the object set to the gameobject property when it is changed, and set the bool based on if it's a prefab or not. It's extra work for writing the editor, but a pro is that you can keep this boolean public which allows the user to define otherwise. So say you did put a live object in and the user wants to dupe it anyways... it's an option.

    Another option is to determine a way to figure out if it's the prefab or the instance at runtime. I've seen people attach a simple script to the base of the prefab that sets a bool true on the Awake message. The prefab never gets initialized, so it'll be false, where as the instances will be initialized and have the bool set true. Then you just search for that component and check the value.
     
    Korno and GibTreaty like this.
  4. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Good idea, you could even make an extensions method for gameObject called IsPrefab() which does all that for you.
     
  5. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class PrefabTest : MonoBehaviour
    4. {
    5.     bool m_IsPrefab = true;
    6.     public bool IsPrefab {
    7.         get { return m_IsPrefab; }
    8.     }
    9.  
    10.     void Awake() { m_IsPrefab = false; }
    11. }
    12.  
    13. public static class GameObjectExtensions
    14. {
    15.     public static bool IsPrefab(this GameObject instance)
    16.     {
    17.         PrefabTest script = instance.GetComponent<PrefabTest>();
    18.         if (script == null)
    19.             throw new System.Exception("Missing PrefabTest script");
    20.         else return script.IsPrefab;
    21.     }
    22. }
    Usage:
    Code (csharp):
    1. public GameObject player;
    2. void Start() {
    3.     if (player.IsPrefab())
    4.         Debug.Log("Player is a prefab");
    5. }
     
    Last edited: Dec 13, 2014
    Korno likes this.
  6. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    Very cool. Thanks for the help. I thought I might have to do something like that, a bit surprised that Unity doesn't provide a property to test this. I managed to also test it by checking if the game object was active but this way seems much cleaner.
     
  7. Rodolfo-Rubens

    Rodolfo-Rubens

    Joined:
    Nov 17, 2012
    Posts:
    1,196
    That's odd. I'm struggling to find a way to do this because I have a method that takes an "object" as parameter and I can pass a prefab or an instance and checking activeInHierarchy (it always returns true for prefabs since that's how they are in the project folder) doesn't work, and in the docs say: "This lets you know if a gameObject is active in the game." ...

    What now?:(
     
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This is a weird problem to me. I've never really structured a game where I didn't know if I was using a prefab or an actual GameObject. I'm interested in the use case where this is applicable, solving the use case may provide a better alternative for structuring your project.
     
  9. Rodolfo-Rubens

    Rodolfo-Rubens

    Joined:
    Nov 17, 2012
    Posts:
    1,196
    Hey @BoredMormon so, the problem is I have a method (GetWeapon (Weapon weapon)) and when I call it want to pass or a weapon that is already in the game, so I can access its gameObject and do whatever I want, or I could also pass a prefab, so I can also access its gameObject, which I dont want to, I want a way to know if the weapon I received on the parameter is an instance already in game or a prefab so I can create a condition in this method so I don't, for example, set it active (which would return a null ref if I do this in a prefab's gameObject), instead if the weapon parameter it is a prefab I want to instantiate. I don't understand it would be a simple task, like gameObject.exists or something like that.

    Edit: In fact, I know when it is a prefab and when is not, I could create two separated methods or even put a bool on the parameters and check this bool inside the method, but still. :/
     
  10. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,381
    Sounds like you need to separate your variables between what is 'library/prefab/reference' and 'runtime/live/game'. I do this with weapons by having a list in the editor where I fill it with the weapons I want, then at Play I'll fill that into a separate runtime list. The only real access to that list is through a couple of functions like GetCurrentWeapon() which returns from the runtime list.
     
    Kiwasi likes this.
  11. callen

    callen

    Joined:
    Dec 31, 2013
    Posts:
    29
    I've been looking for a solution to this too. I saw a few threads with the "there's no use case for this" comment and I disagree. Here's my use case, tell me what a better alternative would be... I want to make a generic reusable object (here its a message box). It may be used in two different ways, depending on my needs at the time. In one usage, I never have multiples of the object, so I put a single instance of the object in my scene, and a manager will simply SetActive() on it to show or hide it as needed. However in a more complicated case where I may use multiple at once, that same manager should be able to instantiate a new object and delete it each time it is used.

    I want my manager to just 'know' my intended usage by assigning the object to a public field. If the assigned object exists in the scene already, I will use the Activate/Deactivate method, and if the object is a prefab I'll Inst/Destroy.

    The issue with putting the Awake() script on the object, is that an object that begins life inactive will not call Awake until activated (in my case this isn't a problem, so this is what I'll use, but it doesn't work as a general solution).

    Even if complicated, has anyone found a solution for this that would work in every single case? I've read in another thread that there aren't really any 'prefabs' at runtime, they are just objects not included in the scene hierarchy - is there really nothing that can differentiate a prefab from a never-activated scene object?
     
  12. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    Well you technically could just have a bool flag on the manager that changed the mode it was in.

    Objects shouldn't be accessed until after Awake is called anyways. During the 'Awake' call, only ever access the self. And during Start, all Awakes have been called.

    If you're accessing these things prior to that... well, stop it. Events exist to work around that.

    If all else fails, use execution order to flag the script as one of the earliest scripts to run.
     
  13. callen

    callen

    Joined:
    Dec 31, 2013
    Posts:
    29
    If I put a flag on the manager, then it will cause issues if incorrectly set (eg UseAsInstance=true, but the assigned object is a prefab, so calling Setactive(true) wont do anything [right?])

    I don't really understand what you're saying here: "Objects shouldn't be accessed until after Awake is called anyways."

    For one, I don't know what you mean by 'accessed' - you suggested putting a public bool on a script, along with something like void Awake() { awoken=true; } This is somehow a horrible thing that needs to be "stopped"?

    Secondly, accessing objects before Awake() is a designed part of the Engine. Lets say I have a parent object that wants to iterate over its children. But one of the children is inactive (and was never previously active) so *horror* its Awake() was never called. Should the transform iterator be removed, lest we accidentally touch these pre-Awake() objects?

    I don't know what exactly you were trying to say, but I'd rather get an answer to my question than admonition for using the Engine in a way for which it works perfectly fine.

    I think this all boils down to this question, so I'll restate it:
    "Is there anything that can differentiate a prefab from a never-activated scene object?"
     
  14. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    Don't configure it incorrectly. You could even write an editor script that determined the 'prefab' status of the referenced object and set the flag accordingly. Since... it is editor time. If you really needed it to do so for "dumb" users.

    What I mean is:
    http://docs.unity3d.com/ScriptReference/MonoBehaviour.Awake.html
    By access I mean to actually read information from another object. You can grab references in Awake... but don't determine any information about them until 'Start', because there's no guarantee that it's been initialized yet.

    Not according to the Unity documentation. It's there to get a reference to an object before Awake has been called, but not actually access its data. It's data may not have been initialized, and therefore not what is expected.

    Obviously it's not perfectly fine, since it isn't doing what you would expect. Rather it's doing exactly as the Unity documentation says it would... so it's best to follow their suggestion about how to order initialization of scripts.

    And this thread covers MULTIPLE methods of doing that. But also explaining the short comings of them.

    Sorry, no, there's no silver bullet!

    This isn't some admonishment of your question. It's pointing out that answers were given. You seem to not like the answers, and are like "this hasn't been answered", completely ignoring those answers that exist.




    Also, an added thing you could do with the script option, is to set the flag 'isPrefab' false when it gets added to the scene. You again could have an editor script that tests this and flags it automatically for you.

    This way the value is set on deserialize, before awake even occurs. But it's still error prone, because the editor script might fail. For example someone might change the flag back to true for whatever reason... and your editor script might not monitor for that.
     
    Last edited: Nov 4, 2015
  15. callen

    callen

    Joined:
    Dec 31, 2013
    Posts:
    29
    "You can grab references in Awake... but don't determine any information about them until 'Start', because there's no guarantee that it's been initialized yet."

    I really don't understand what you're thinking when you say "references" versus "data" because they are the same thing - to have a reference to a component allows you to determine at least one piece of data - whether or not the component exists. I'm going to guess that you mean "data" like physics properties, or other 'heavy' component properties?

    Also, I never said "...it isn't doing what I would expect." Additionally as I just found out, it doesn't do what the docs say either.

    There have been, so far, zero explanations of how to do this without manually entering the data. I'm not looking for a 'silver bullet' so much as a generic way to do this for an object whether it has my own scripts on it or not.

    I even thought I had found it... because you get an error when trying to assign a prefab's transform.parent, saying "Setting the parent of a transform which resides in a prefab is disabled to prevent data corruption."

    But alas, this isn't an exception (which you could catch) but just an error message thrown internally by Unity - and one that doesn't get emitted outside the editor either, it seems.
     
    Last edited: Nov 4, 2015
  16. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    By data... I mean data.

    A reference is a pointer to an object that contains data.

    Data is the properties/fields on the object.

    Objects encapsulate data.

    This is the common nomenclature used when discussing object oriented structures. Sure, we could get into a semantics debate here... but come on... I'm not making words up here. You just don't subscribe to the definitions I'm using, for whatever reason.

    There has been zero explanations about how to use some inline unity method to do it... because there is no inline unity method to do it.

    See, if unity did have some inline way of doing it. There'd be data entered by unity somewhere to test. Since they haven't, YOU need to include the data to use to test by.

    Sorry if you don't like that, that's just the way it is. Unity does not include some marker to identify prefabs from non-prefabs. So thusly you have to create some marker.

    Oh dear, do not depend on some weird exception that might occur to determine the status of an obejct.

    Really... I don't care anymore. You continue having your fun attempting to hack your way out of the problem, and ignoring the MULTIPLE solutions given.
     
  17. callen

    callen

    Joined:
    Dec 31, 2013
    Posts:
    29
    You have fun with your anger issues. This line was hilarious to me:
    "Oh dear, do not depend on some weird exception that might occur to determine the status of an obejct." Actually, this is a pretty common design pattern for I/O, null-ref checking, etc, in the whole .Net library. I guess Microsoft taught me wrong.

    I got pretty close to a solution based on my last insight:

    Code (CSharp):
    1.  
    2.     public static bool isPrefab(GameObject gob)
    3.     {
    4.         if (temp == null) (temp = new GameObject("prefab-tester")).SetActive(false);
    5.         Transform par = gob.transform.parent;
    6.         gob.transform.parent = temp.transform;
    7.         if (gob.transform.parent != temp.transform)
    8.             return true;
    9.         gob.transform.parent = par;
    10.         return false;
    11.     }
    12.     static GameObject temp = null;
    Unfortunately, while this does work in the editor it still does NOT work in a compiled build. Maybe someday I'll learn why, I've got $1 for anyone who can enlighten me.
     
  18. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    Null ref checking, sure.

    But accessing an object and seeing if it throws an exception to determine its state?

    NO, not considered appropriate practice. Not at all by MSDN standards.

    You only do it if it's the only option you have (such as some I/O). In the same respect the exception thrown is related.

    You described a dependence on an unrelated exception, in hopes of deducing that it might be a prefab.
     
  19. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    I'm honestly not surprised. None of what is going on their is related to something being a prefab.

    Sure, it might happen to react in a way that allows you to indirectly deduce the possibility that it's a prefab. But Unity might change its practice at any time in relation to that (note, it doesn't work at runtime).

    This code would be completely unsafe and indeterminate. Nevermind slow since changing the transform parent is relatively expensive.

    But hey, if you find some method to do this that makes you happy. Go for it. Have fun.
     
  20. callen

    callen

    Joined:
    Dec 31, 2013
    Posts:
    29
    Why are there so many people like this in the Unity community? This is the only development community I've ever seen that loves complaining about design / style more than gaining a deeper understanding of the APIs they use. o_O

    There are lots of ways to skin a cat, they say. But I think here they would say "No no, first you have to refactor your cat such that you have a skinless cat, and separate cat skin. Then you're already done, what's the trouble?!"

    And I could only imagine the horror at using a javascript shim function - "Just because it works sometimes, the browser might change its practice at any time, so you should never do it!!!"

    So thanks for reminding me why I don't try to involve myself in this miserable, toxic community.
     
  21. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    What actually happens in the build? As indicated by @lordofduct its an ugly hack of a solution. But I'm curious anyway. Does the prefab actually get parented to the empty object? Does it throw an exception?

    Easy Tiger. He's only trying to help.
     
  22. callen

    callen

    Joined:
    Dec 31, 2013
    Posts:
    29
    If you use that method I posted, what happens is...
    In the editor: when you try to set the parent, it remains null, and in the console you get that error message
    In the compiled build: setting the parent works, ie the == test returns true, so the prefab object acts just like the instance. No errors are logged and no exceptions are thrown. The prefab object does not appear in the scene tho.

    Based on this functionality and the error message's clue "disabled to prevent data corruption", I'm guessing it works something like this: Prefabs, like any object, can have a parent, but when you try to set that parent, there must be a check - probably using the editor-only PrefabUtility.GetPrefabType. This check then cancels the operation and logs an error. In the compiled build, there really isn't any concept of "data corruption" in that sense, and the editor functions are gone, so there is no reason/ability to do the check.

    This leads me to really wonder what the Unity "scene" is, especially at runtime... How does it know that my prefab, parented to a scene object, is not itself a scene object?

    I'm a bit burnt out on this question for right now, but the next thing I would test is whether or not the scene-object that I parented to will get a reference to the prefab if doing something like foreach(Transform t in transform)

    As for him "trying to help" its about tone, you know? Telling me there's no answer is one thing, but its insulting to say I'm pursuing an "unsafe" solution that's "not considered appropriate practice" in a language I have 10+ yrs experience in.
     
  23. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    Tone in a toneless medium?

    Experience that I am not aware of. Just as you are unaware of my multiple years of experience.

    "unsafe" and "not considered appropriate practice" are legitimate criticisms of code, when other options have been offered up in the thread that are safer and abide by the design principals put forth by the API you're using. And I didn't say you can never do it... actually quite the opposite, I said you could if you wanted to, but highlighted the issues with it.

    Nor did I say there was "no answer", I said that unity didn't have a built in function that did it. And highlighted the multiple methods of accomplishment that are through out this entire thread. I repeatedly referenced them and even suggested editor script expansions to them to make them even more user friendly. And you repeatedly responded saying that I'm saying there's no answer... how are the multiple answers not answers?

    Sorry if you felt I came off rude and condescending... in the same respect, I could say the same about you.
     
  24. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,381
  25. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    As far as I am aware prefabs are an editor only concept. At runtime prefabs are identical to regular GameObjects. The main difference is that they haven't been initialised. Hence the comments about setting a bool in Awake. Unity might have some internal flag you can poke at with reflection to see if the object has been initialised, but its likely that its handled entirely on the C++ side.
     
  26. callen

    callen

    Joined:
    Dec 31, 2013
    Posts:
    29
    That will only work if the object is active though, prefabs and inactive scene objects will both have this set to false, I believe.

    What @BoredMormon is saying makes sense compared to what I've been seeing.

    Hopefully if/when they address other prefab issues (nesting prefabs, viewing a grand-child of a prefab...) this secret internal flag (or whatever they do) could be exposed. Instead of only having 'activeInHierarchy' maybe they could add a bool that's just 'inHierarchy'.

    Also the phrase "initialized" keeps being used, but what does this actually mean in a gameobject? Is this where Awake is called, or is calling Awake() a separate step that happens after this initialization? I'd have to guess the latter based on this behavior I asked about, so is 'initialization' an internal concept that only the engine knows about?
     
  27. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    Initialization is after the object is ready to be accessed.

    This may include 'Awake' if awake sets anything up.

    The word is thrown around a little arbitrarily in the unity documentation.

    Most often it implies that the GameObject has been created, its components added, its fields deserialized, and Awake called.
     
  28. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,381
    Execution Order in Unity FYI.

    I think this is a fairly fringe case you have here with spawning an inactive object and needing to test if it is a prefab. That sounds like something could be better in the design.

    If the GameObject is active, you can use GameObject.activeInHierarchy.

    You can also make a flag in Awake() to say if something is 'aliveInScene' or whatever.

    Lastly, you can do GameObject.Find, but thats silly while you have other good options.