Search Unity

Reference to prefab changing to clone self-reference

Discussion in 'Scripting' started by rsx, Aug 13, 2010.

  1. rsx

    rsx

    Joined:
    Nov 4, 2009
    Posts:
    87
    Hi,

    I have a script on a prefab which displays a GUI button. When the button is clicked it instanciates a prefab. The prefab to instantiate is a drag dropped reference on a transform parameter in the script.

    This works.

    However... If I want an instance (clone) of the prefab to instanciate another clone of its prefab (meaning the reference in the prefab's script is referencing itself), I run in to a problem. When I drag the original prefab in to the scene, everything looks fine, but the when I use the UI to make an instance/clone, and look at the new clone in the inspector, the reference is no longer to the prefab, it is a self-reference to the clone in the scene.

    This is a pretty simple setup, but kind of hard to explain. I hope I am making sense.

    My goal is to have this game object make copies of itself using the GUI Button.

    My GUI code is simple. It does not set this reference anywhere in the code. Something is triggering the change, but I don't know where to start my search. Hopefully someone here can help. In the mean-time, I'll start stripping back my code to see if I can uncover it.

    Cheers,
     
    rustinlee likes this.
  2. AkilaeTribe

    AkilaeTribe

    Joined:
    Jul 4, 2010
    Posts:
    1,149
    ?

    If you showed your code, we could have helped, but simply telling us this don't help you at all, you're still alone against your problem.
     
  3. rsx

    rsx

    Joined:
    Nov 4, 2009
    Posts:
    87
    I was hoping someone might have seen something like this before and have an idea where I might look. I have a lot of code to trip back and it is bed time here.

    It is strange the code works fine with one object but not another. My main lone is a simply:

    Transform instance = (Transform)Instantiate(prefab);

    Where 'prefab' is the reference right from the inspector. After I make the instance, I do a bunch of manipulation on the transform, but nothing else really.

    I'm sure I can isolate it if it is in my code, but last time something weird happened like this, it turned out to be a Unity bug that cost me 6 hours, so I did some high-level repro and decided to post here. I promise to go deeper in to my code tomorrow.

    Thanks for the reply.
     
  4. rsx

    rsx

    Joined:
    Nov 4, 2009
    Posts:
    87
    OK, I have a repro.

    1. Create a new scene.

    2. Create a sphere, make it a prefab, and add the prefab to the scene.

    3. Add the following script to the sphere prefab.

    4. Drag the sphere prefab onto the script's "prefab" attribute in the inspector.

    5. Play the game and click the button over the sphere to create another sphere.

    6. Look at the new sphere's script's "prefab" reference and it will NOT be referencing the prefab, instead it will reference the instance that created it.


    Code (csharp):
    1. using UnityEngine;
    2.  
    3.  
    4. public class TestInstanceButton : MonoBehaviour
    5. {
    6.     public Transform prefab;
    7.  
    8.  
    9.     protected void OnGUI()
    10.     {
    11.         float height = 30;
    12.         float width = 70;
    13.  
    14.         // Create a new rect
    15.         Rect rect = new Rect();
    16.         rect.height = height;
    17.         rect.width = width;
    18.  
    19.         // Put the rect over the transform
    20.         Vector3 pos = UI.cam.WorldToScreenPoint(transform.position);
    21.         pos.y = Screen.height - pos.y;
    22.  
    23.         // Multiply by half so the rect is centered
    24.         rect.x = pos.x - (width * 0.5f);
    25.         rect.y = pos.y - (height * 0.5f);
    26.        
    27.         // Create the GUI
    28.         GUILayout.BeginArea(rect);
    29.  
    30.         if (GUILayout.Button("test", GUILayout.Height(height)))
    31.         {
    32.             Transform instance = (Transform)Instantiate(this.prefab);
    33.  
    34.             if (!instance)
    35.             {
    36.                 Debug.LogError("Unable to create instance.");
    37.                 return;
    38.             }
    39.  
    40.             // Move the instance over to the side a little so we can see it
    41.             Vector3 new_pos = new Vector3();
    42.             new_pos.x = transform.position.x + 3;
    43.             new_pos.y = transform.position.y;
    44.             new_pos.z = transform.position.z;
    45.             instance.position = new_pos;
    46.         }
    47.  
    48.         GUILayout.EndArea();
    49.     }
    50.  
    51. }

    No where in the code do I change the reference, so it is definitely Unity doing it.

    If I need a workaround, Does anyone know a way to get to a prefab from an instance/clone of it?
     
  5. cj-currie

    cj-currie

    Joined:
    Nov 27, 2008
    Posts:
    337
    I have this same issue, and I really want to get it fixed. For me, it's a slightly different situation, though.

    I have a script that goes on an Item object, and when the player collides with it the Item is added to the Inventory and the object is destroyed. The Item also had a variable, myPrefab, that pointed to the prefab that it is a copy of. This reference is copied into a holder var before the object is destroyed. Tests on the holder var confirm this.

    However, at run time, the variable points not at the prefab in the Project folder, but to the actua in-game GameObject. This makes sense because at run time they are essentially clones of each other, and instantiating one over the other makes no difference, but if I store the variable myPrefab in another script and try to instantiate it later, the changed reference means I'm trying to instantiate an object that was destroyed - which is against the rules.

    Basically,
    1.) Object 1 stores a reference to its own prefab.
    2.) At run time that reference changes from the prefab in the Project folder to the in-scene instance of the object.
    2.) Object 2 copies that reference and destroys object 1.
    3.) When Object 2 tries to instantiate using that reference, it gets a NullReferenceException because the object no longer exists.

    I have a theory that if I can somehow break the prefab connection before Unity does step 2.), that would fix this.
     
  6. AkilaeTribe

    AkilaeTribe

    Joined:
    Jul 4, 2010
    Posts:
    1,149
    Pretty crazy.

    If you don't set anything in the "prefab" Transform value of the prefab...

    And you set the prefab in the "Prefab" Transform value of the sphere in the scene...

    It works as intended.

    Putting the Transform prefab in the "prefab" Transform value of the prefab (its own Transform) results in the current situation (cubes are instantiated from the previous instance).

    I think the use of a third object is required. This object has a reference to the initial gameObject, and whenever a instance is wanted, the prefab checks for the gameobject reference, his "prefab" variable (which should not chance over time).
     
  7. nvarcha

    nvarcha

    Joined:
    Sep 27, 2008
    Posts:
    191
    Make sure you're selecting a Prefab from the Project and not from the Scene.

    Hope this helps.
     
  8. rsx

    rsx

    Joined:
    Nov 4, 2009
    Posts:
    87
    @nvarcha Yeap.

    @cjcurrie The reference is good if the gameobject is placed in the scene then the game is started. It only seems to happen if the gameobject is instanced during the game play.

    My problem is that this game object has a starting state in the prefab, and once added to the scene, will change. Making a copy of the copy, with the changed state, really messes things up.

    @AkilaeTribe I can't use that work-around that because these objects are placed by a user. However, you might have given me a workaround I can use, it just seems a bit insane...


    Work-around? (still need to try this tomorrow):
    I already have a "Globals" game object I use for other stuff, this is in the scene before the game starts and I suppose I could place a script on there that holds references to the prefab. Any script that needs to make instances of these references could stor a reference to "Globals" on Awake() then instance with that reference instead of the local script drag-and-drop reference.

    If this works, I would likely go one step further and make a globally-accessible static dictionary (singleton), so I don't have to do anything in Awake(). The dictionary would be created on startup and used globally.




    A Comment to the Unity Team:
    I'd like to say this is not the expected result from a user point of view. This creates a black-hole of debugging time. I can easily store a reference to an instance from a script, I don't need some elaborate reference passing to try and solve this. Especially when I don't need it.

    I expect a prefab to always instance a fresh copy, not a copy of a copy. Would you expect a C# class to create an instance of the last instance? Or a new instance? The Prefab holds an initiated state, and THAT is what I want to instance in to my game.

    I love Unity, but this kind of thing really eats away my day. I hope you will either explain why this is a wanted feature, in clear terms, to me and in the docs; or fix it.

    I mean this to be constructive.


    Cheers,
     
    teremy, t-heo, M_R_M and 1 other person like this.
  9. AkilaeTribe

    AkilaeTribe

    Joined:
    Jul 4, 2010
    Posts:
    1,149
    I think it works like this because the variable refers to a component that is a part of the game object itself.

    The reasoning must be the following :

    - If the component pointed is on another gameObject, there is nothing special, it will always show "that" component.
    - If the component pointed is on the same game object, then, it will understand it is his component and refers to itself.

    Another example is Microsoft Office Excel. When you make a table, with three columns, the third columb being the sum of the two first columns, you write :
    A1 + B1 = C1

    Excel is clever, if you copy paste this line deeper in the table, it changes the numbers. So, Ax + Bx = Cx. It is relative position.

    There is an option in Excel, that is not present in Unity, blocking value.

    So, writing in Excel, for example, $A$1 + B1 = C1, and copying pasting it will result in the general formula : A1 (always) + Bx = Cx.

    But let's go back to the subject. If what I wrote is true, then, you really need a foreign object, to avoid the relative position. :?
     
  10. cj-currie

    cj-currie

    Joined:
    Nov 27, 2008
    Posts:
    337
    A simple "break prefab reference at startup" or "don't make the assumption that I meant your own object" method would solve this.
     
  11. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    If you want to instantiate the prefab itself, place it in the Resources folder and do a:

    Instantiate(Resources.Load("PrefabName")) as GameObject;

    Once you drag a prefab into the game world, it's an instance of that prefab - anything you do down the line will be done to that instance. It sounds like you are attempting to use prefabs in a way that they are not designed to be used.

    To do something similar to what you want, here is what I would do:

    1) Make a script on all of my prefabs I wish to instantiate this way with some value unique to that prefab (I'd probably just use a string equal to the prefab name).

    2) Move all the prefabs to the resources folder, or a sub-folder

    3) Make my script which does the instantiation do something like:

    Instantiate(Resources.Load("Prefabs/" + myClass.PrefabName)) as GameObject; (where PrefabName is a public/getter string equal to the name of the prefab object in the resources/Prefabs folder).
     
  12. cj-currie

    cj-currie

    Joined:
    Nov 27, 2008
    Posts:
    337
    I was aware of Resources.Load, but I didn't want to muck with a resources folder :p
     
  13. Ezzerland

    Ezzerland

    Joined:
    Jul 1, 2010
    Posts:
    405
    The problem with having to find a work around is that this should not need a work around.

    There is no reason that an object cannot store it's own prefab as a variable to later be instantiated.

    In fact, this does work. Upon running this with some debug settings, it successfully passes the prefab name that it's supposed to instantiate to be stored. Problem is, as mentioned in the topic, is it stores it as the clone and not as the prefab itself, so when called on, the prefab in clone form does not exist, and thus fails.

    IMHO, this is clearly a bug, as it ONLY happens when the instance refers to itself.

    the most "optimized" fix for it, at least now, is to create 2 prefabs of the same object. object1 references object2, and object2 references object1.

    I would definitely submit this to support as a bug, with example of code.

    Regards,
    Rob
     
    rustinlee and M_R_M like this.
  14. cj-currie

    cj-currie

    Joined:
    Nov 27, 2008
    Posts:
    337
    Submitted. UT, please fix this asap!
     
  15. rsx

    rsx

    Joined:
    Nov 4, 2009
    Posts:
    87
    Exactly my thought. If it did this in all situations it would be a feature, but it only happens in this specific situation, so it has to be a bug IMO. Inconsistency is bad.

    I thought Unity guys where on these forums. I'll look for an official place to post this as a bug.

    Nice idea using a duplicate. Not great, but the resources folder scares me for some reasons, lol. I just hate workarounds that increase the chances for human error. I'm still thinking a global script to hold prefabs is the way to go, then we keep one prefab, with only one state possible, and I can print error messages to protect my code.

    Thanks for talking through this guys.
     
  16. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,990
    It's not a bug in my eyes. It's kind of a feature that cause some problems in this special case. A prefab is a very special construct. You can create very complex prefabs with a lot of nested gameobjects and scripts. For example: if you have a script with a public var of type Collider, you can assign the collider of eg. a nested gameobject to that variable. The clue is when the prefab is instanciated all local references you've set inside the prefab will be adjusted to match the new clone. Consider this: you've created a house with a door and a trigger that opens that door. You would have set a reference in the script on your trigger to the door GameObject. When cloning the prefab of the house (or even the house itself) those internal references will be adjusted during Instanciate().

    In most cases it's a good feature and saves a lot of time to init all references at Start(), only when you try to make a self replicating prefab you run into that problem. Maybe you can copy the prefab reference into a static variable at Awake() (only if not set yet) and use that for replication. But i will only work when assigning the prefab to an instance in the scene and you do NOT apply the changes. If you press apply the references get immediately adjusted. That means the true reference to the prefab is not set inside the prefab it's on that one single instance in the scene.

    I think the best way/workaround would be some kind of singleton prefab manager. Maybe Unity will add another bool to Instanciate() to specify whether you want to adjust references or not. But i think that's not that trivial because even the transform child relationship of the cloned objects have to be adjusted. Otherwise there would be a child with two parents or the original will loose the child to the clone.
     
    Olipool and Pacosmico like this.
  17. Jesse Anders

    Jesse Anders

    Joined:
    Apr 5, 2008
    Posts:
    2,857
    (This thread is actually a few months old...)

    Maybe there's a good reason for it to work this way, but it certainly cost me some debugging time the first time I ran into it. What I do now whenever I need 'recursive instantiating' is create two identical prefabs and link them to each other. It's not ideal (since you have to keep the prefabs in sync), but it works.
     
  18. gameboyyo

    gameboyyo

    Joined:
    Jul 16, 2011
    Posts:
    20
    What an horrible issue. Just stumbled upon it. Stupidly absurd. Working around it requires an equally contorted and retarded approach to this which should be a non-problem.
     
    Omti1990 likes this.
  19. vladk

    vladk

    Joined:
    Jul 10, 2008
    Posts:
    167
    My workaround for this one is that I make a host-object that instantiates a prefab in the position the original gameObject requires it to be. Quite painful tho. A fix required!

    And no, there is no "good reason for it to work this way". It's a freaking bug for Christ sake!
     
    Omti1990 likes this.
  20. Ad1981

    Ad1981

    Joined:
    Dec 14, 2011
    Posts:
    1
    I had similar problem with buildings which can be destroyed, and repaired (replaced by fresh instantiation of prefab). The easiest way, which i found to resolve this was to create in first frame after start copy of object and deactivate it. Then in moment, when i normally would call instantiate, i'm just activating previously created one.
     
  21. idmadj

    idmadj

    Joined:
    Jan 17, 2012
    Posts:
    1
    This looks like a design compromise the Unity team had to make. It's helpful in some cases, not in others.

    Instead of going one way or the other to "fix" this issue, why not create a new attribute? Something like "@ExplicitReference" or whatever. Leave the default behavior as it is, and if we want the field to always point to a prefab in our asset folder (instead of the one on the scene), we'd just add this attribute.

    This would also prevent older code from breaking.
     
  22. GBCFraser

    GBCFraser

    Joined:
    Apr 11, 2013
    Posts:
    94
    I hate to bump an old post, but its better than starting a new one... has anyone found a solution to this issue besides the ones mentioned? I just ran into this issue, any planned updates by unity to fix this?
     
    t-heo likes this.
  23. Glorion13

    Glorion13

    Joined:
    Nov 19, 2012
    Posts:
    8
    The quick dirty solution is the one mentioned about creating 2 objects, each pointing to the other. Another solution would be to have a 2nd, different object, which points to the prefab. Then the original prefab can point to where that 2nd different object points.

    Example:
    You have a 'Building' prefab, which can build new 'Building' objects and you have it pointing to itself, creating the Clone issue.
    You create a 2nd object, called 'ListOfBuildings' and you have it reference the 'Building' prefab. Then, the 'Building' prefab, instead of referencing itself, will reference the 'Building' prefab from 'ListOfBuildings'.

    I hope this makes sense.
     
  24. CiberX15

    CiberX15

    Joined:
    Jul 31, 2013
    Posts:
    15
    I don't feel bad about dragging this back to light. It is causing massive problems for me and apparently many others and needs to be complained about properly so that the devs at least keep trying to fix it. : P

    Like this perhaps?!

     
  25. bombapps

    bombapps

    Joined:
    Jul 11, 2013
    Posts:
    5
    Yeah I've gotta say this is a huge pain.

    I want to copy the original reference to the prefab because it saves me having to reset all the changes over the lifetime of the currently active one, but as soon as destroy the active object, presto, all the scripts are deactivated. None of the suggested workarounds are very practical and make it very messy to maintain if you are dealing with more then just a few prefabs.
     
  26. fivearchers

    fivearchers

    Joined:
    Apr 17, 2009
    Posts:
    716
    Ugh just hit this wall also. Kind of a stupid bug. I'm going to try the two object method I think (though I may end up with resources load).
     
  27. bombapps

    bombapps

    Joined:
    Jul 11, 2013
    Posts:
    5
    Yeah the two object method works, though it is cumbersome. I ended up using a combination of two objects in some cases and in others I used a small script to enable all children components in Awake(), which works fine if you know all the types of the components you need to enable (in my cause just MonoBehaviour). Won't work in all scenarios of course.
     
  28. Devil_Inside

    Devil_Inside

    Joined:
    Nov 19, 2012
    Posts:
    1,119
    Oh God! A 3 years old bug still not fixed.
    Spent the last 2 hours trying to understand what I'm doing wrong.
     
    Omti1990 and lPVDl like this.
  29. cAyouMontreal

    cAyouMontreal

    Joined:
    Jun 30, 2011
    Posts:
    315
    Same issue here, an object can't keep a reference to its own prefab...
    Any news about that?

    Or is there a way to get the prefab referenced by using a "magic" method?
     
  30. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,656
    The behavior is by design, it's not a bug.

    The solution is usually to have the thing that did the instantiating (and therefore has a reference to the 'original' prefab) fix up the instantiated object. i.e:

    Code (csharp):
    1.  
    2. MyObj instantiatedObj = (MyObj) Instantiate(prefab);
    3. instantiatedObj.sourcePrefab = prefab;
    4.  
     
    Pacosmico likes this.
  31. basti

    basti

    Joined:
    Jan 5, 2012
    Posts:
    6
    could you please elaborate further? If i pass the reference to the original prefab over to the freshly instantiated copy it does not work for me.

    Code (csharp):
    1. public class SelfInstantiate : MonoBehaviour
    2. {
    3.     public SelfInstantiate myPrefab;
    4.    
    5.     public int testVal1;
    6.     public int testVal2;
    7.    
    8.     void Update ()
    9.     {
    10.         if(Input.GetKeyUp(KeyCode.Space))
    11.         {
    12.             SelfInstantiate si = (SelfInstantiate)Instantiate (myPrefab, Vector3.zero, Quaternion.identity);
    13.             si.myPrefab = myPrefab;
    14.         }
    15.     }
    16. }
     
  32. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    230
    Is there a reason you are assigning to `sourcePrefab` ? Why not just set `instantiatedObj.prefab = prefab;` like basti is doing?
     
  33. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,656
    They're just different variable names.
     
  34. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    230
    You are not setting the same field on the instantiated object (sourcePrefab) that you get from the current object (prefab)

    instantiatedObj.sourcePrefab = this.prefab

    Did you mean

    instantiatedObj.sourcePrefab = this.sourcePrefab ?

    Whereas basti is setting/getting the same variable, and reporting a failure.
     
  35. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,656
    My code snippet isn't designed to be used inside an instance of MyObj; it's designed to be used inside the instance of the thing creating MyObj (say, MyObjFactory). So there's no reason why the field names would necessarily be the same.

    I'm not sure why basti's code isn't working. Needs to be more specific than 'it does not work for me' if we're going to diagnose it.
     
  36. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    230
    Ah okay, that confused me :)...

    Just staying a little more on topic. We used the workaround of creating a Singleton with references to all the prefabs, so we

    Code (CSharp):
    1. (Tree) Instantiate(  PrefabHolder.Instance.TreePrefab )
     
  37. StevenMarky

    StevenMarky

    Joined:
    Apr 24, 2013
    Posts:
    3
    How to reproduce:
    1. Create a new script (C#)
    2. Add a variable: public GameObject myPrefab;
    3. Create an empty GameObject in the scene
    4. Attach the new script
    5. Drag the game object into the project window to create a new prefab
    6. Now, drag the newly created prefab in the project view to the myPrefab variable in the scene view
    7. If you click the 'myPrefab' value at this point (in the scene), you'll see that it highlights the prefab in the project view (correct)
    8. Click Apply on the GameObject
    9. If you click the 'myPrefab' value at this point (in the scene), you'll see that it highlights the gameobject itself in the scene view (unexpected). This means that the script cannot create new instances of the prefab, only clone itself.

    Is that really intentional? Although you can posit a self-instantiating gameobject isn't good design, it still seems like a bug (or is there a good reason it behaves like this?).

    This happened in 4.6.2 on Windows and Mac.

    Also superpig - hey! I used see you frequently in #gamedev many years ago (I was stevenmarky).
     
    phobos2077 likes this.
  38. dogzerx2

    dogzerx2

    Joined:
    Dec 27, 2009
    Posts:
    3,967
    Not sure if this helps, but you can use PrefabUtility.GetPrefabParent(yourPrefabInstance);

    This will get the real prefab, from your assets... instead of the prefab instance you've dropped on the scene.

    EDIT: Actually doesn't work once you make a build.
     
    Last edited: Jul 14, 2015
    io-games likes this.
  39. lPVDl

    lPVDl

    Joined:
    Aug 10, 2014
    Posts:
    8
    Asked about it in Unity questions but not see it posted, looks like devs don't like such questions about self linked prefabs. Thanks community for a method of two objects :)
     
  40. simplicitydown

    simplicitydown

    Joined:
    Dec 11, 2015
    Posts:
    6
    this thread needs re-reviving... There's still no fix for this and it is now five years or so later from the first person posting about this. I'm having the exact same problem, and I'm going to try the two object method now, but it seems weird.
     
    dogzerx2 likes this.
  41. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    230
    If you instantiate a game-object with a child, references (on the new object) to the child will point to the child of the new object. I.e. References are updated relatively to the new object. So a "self-reference" is updated to be a "self-reference" on the new object too. This is the thinking behind the mechanic.
     
  42. gspeager

    gspeager

    Joined:
    Jan 11, 2016
    Posts:
    23
  43. ThePilgrim

    ThePilgrim

    Joined:
    Apr 25, 2013
    Posts:
    17
    I solved this problem and avoided double prefabs by creating a singleton in which I dragged in the prefab references to all of the prefabs contained my script in which I needed a self-reference.

    On the self-referencing script, in the place of a reference to its own prefab, I instead added a string field called uniqueIdentifier. Each different prefab needs to have a unique value for this field. This can be defaulted to the name of the prefab if need be.

    Since the singleton has a reference to each of the prefabs, and the prefabs contain the script with the unique identifier, the singleton go through each of it's prefab references, get the component, and read that uniqueIdentifier string. With this, it can build a dictionary which links the string identifiers to the desired prefab.

    Now, when the scripts need to reference their own prefab, they can ask the singleton what prefab is associated with their uniqueIdentifier.

    Example code:
    Code (CSharp):
    1. public class SelfReferencePrefabManager : MonoBehaviour
    2. {
    3.  
    4.     public GameObject[] prefabs;
    5.     private Dictionary<string, GameObject> identifierToPrefabDictionary = new Dictionary<string, GameObject>();
    6.     public static SelfReferencePrefabManager instance;
    7.  
    8.     void Awake ()
    9.     {
    10.         instance = this;
    11.     }
    12.  
    13.     void Start ()
    14.     {
    15.         // Build dictionary
    16.         foreach (GameObject prefab in prefabs)
    17.         {
    18.             // get the script with the identifier string
    19.             SelfReferencingComponent selfReferenceComponent = prefab.GetComponent<SelfReferencingComponent>();
    20.             if (selfReferenceComponent != null)
    21.             {
    22.                 identifierToPrefabDictionary.Add(selfReferenceComponent.uniqueIdentifier, prefab);
    23.             }
    24.         }
    25.     }
    26.  
    27.     // Get the prefab by the string identifier. Returns null if it is not in the dicitonary.
    28.     public GameObject GetPrefabByIdentifier (string uniqueIdentifier)
    29.     {
    30.         GameObject prefab;
    31.         identifierToPrefabDictionary.TryGetValue(uniqueIdentifier, out prefab);
    32.         return prefab;
    33.     }
    34.  
    35. }
    36.  
    37. public class SelfReferencingComponent : MonoBehaviour
    38. {
    39.     public string uniqueIdentifier;
    40.  
    41.     public GameObject myOwnPrefab
    42.     {
    43.         get
    44.         {
    45.             return SelfReferencePrefabManager.instance.GetPrefabByIdentifier(uniqueIdentifier);
    46.         }
    47.     }
    48.  
    49.     void Start ()
    50.     {
    51.         // fail early in case we forgot to add the singleton to the scene
    52.         UnityEngine.Assertions.Assert.IsNotNull(SelfReferencePrefabManager.instance);
    53.     }
    54.  
    55. }
     
  44. ZDurst

    ZDurst

    Joined:
    May 1, 2014
    Posts:
    1
    If anyone is still running into something similar (I did), this is a pretty easy solution:
    i.e., if instantiating a new object on destroy:
    Code (CSharp):
    1.     void OnDestroy() {
    2.         YourScriptName yourScriptVariable = (Instantiate (prefabVariableName, new Vector3(x, y, z),Quaternion.identity)).gameObject.GetComponent<YourScriptName>();
    3.         yourScriptVariable.prefabVariableName = prefabVariableName;
    4.     }
    Where prefabVariableName is a variable referencing the original prefab you want to continue to use
    YourScriptName is the name of the script you are in with the above variable and doing the instantiating
    yourScriptVariable is a new variable created for holding the new prefab. You could shorten this into one line, but I like to think this is slightly easier to understand.

    We're instantiating the new copy and grabbing the script and putting it into a variable. We then access the prefab variable in the copy's script to point it at the one we were originally using, rather than at itself.
     
  45. maxizrin

    maxizrin

    Joined:
    Apr 13, 2015
    Posts:
    20
    Hit this bug when trying to grow mushrooms.
    Each mushroom holds the main mushroom prefab... or so I thought, but they spawned with the age of the first mushroom.

    Very strange, as I vaguely recall this working correctly on older versions (4.6 I think).

    I tried encapsulating the prefab in an array, this didn't work either.
     
  46. alxcancado

    alxcancado

    Joined:
    Aug 17, 2013
    Posts:
    13
    Same here. I just want a unique Game object based on my prefab, not a clone reference.
     
  47. Cirrocumulus

    Cirrocumulus

    Joined:
    Apr 9, 2017
    Posts:
    28
    I have just bumped into this issue while working on an Asteroids clone where I wanted the asteroids to be able to split themselves. IMHO the method described four years ago by Richard "Superpig" Fine above is the easiest one to implement, no wrappers or singleton or spawners or Resources.Load or anything. I just tried it and it works really well.

    Here's my code as a more concrete example of what Superpig suggested. The asteroid prefab in the Project has a script component attached to it, called CloneWhenKilled. Other stuff that the asteroid does are inside other components which are irrelevant here. In my GameManager, or wherever you want, where I initially spawn the prefab I do this:

    Code (CSharp):
    1.  
    2. public class GameManager : MonoBehaviour
    3. {
    4.    // Inspector field
    5.    // Just using the component name as a shortcut
    6.    public CloneWhenKilled asteroidPrefab;
    7.  
    8.    public void SpawnAsteroids(int count)
    9.    {
    10.        for (int i = 0; i < count; i++)
    11.        {
    12.            CloneWhenKilled asteroid = Instantiate(asteroidPrefab, Vector2.zero, Quaternion.identity);
    13.            // You have to remember to add this line
    14.            asteroid.SourcePrefab = asteroidPrefab;
    15.        }
    16.    }
    17. }
    18.  
    The CloneWhenKilled component looks like this, which allows a great degree of control over the spawning in the Inspector, right where you'd be looking for it. Obviously you don't need all the parameters I've added (counting spawn generations, adjustable number of clones, using the IKillable interface to call the Kill() method, naming the objects etc). I've also used SourcePrefab as a property rather than a variable but all that is not important. The key here, like Superpig mentioned, is to assign the SourcePrefab property above, and then in the actual script itself, right after Instantiate:

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class CloneWhenKilled : MonoBehaviour, IKillable {
    5.  
    6.     //
    7.     // Inspector fields
    8.     //
    9.     public int numberOfClones;
    10.     public int generationsMax;
    11.     public float scalingFactor;
    12.  
    13.  
    14.     //
    15.     // Private fields
    16.     //
    17.     private float newScale;
    18.     private int _generation;
    19.  
    20.     //
    21.     // Properties
    22.     //
    23.     public int Generation {
    24.         get { return _generation; }
    25.         private set
    26.         {
    27.             _generation = value;
    28.             gameObject.name = "Asteroid (Generation " + Generation + ")";
    29.         }
    30.     }
    31.  
    32.     public CloneWhenKilled SourcePrefab { get; set; }
    33.  
    34.     private void Awake()
    35.     {
    36.         Generation = 0;
    37.     }
    38.  
    39.     public void Kill()
    40.     {
    41.         if (Generation < generationsMax)
    42.         {
    43.             for (int i = 0; i < numberOfClones; i++)
    44.             {
    45.                 CloneWhenKilled clone = Instantiate(SourcePrefab, Vector2.zero, Quaternion.identity);
    46.  
    47.                 // Same important assignment here:
    48.                 clone.SourcePrefab = SourcePrefab;
    49.  
    50.                 // From here on you can do whatever you want as usual
    51.                 clone.gameObject.transform.position = transform.position;
    52.                 clone.Generation = Generation + 1;
    53.                 newScale = Mathf.Pow(scalingFactor, Generation + 1);
    54.                 clone.transform.localScale = new Vector3(newScale, newScale, 1);
    55.             }
    56.         }
    57.     }
    58. }
    59.  
    BTW I'm only defining the prefab as being of type CloneWhenKilled rather than of type GameObject because it saves me two or three GetComponent calls.

    This issue took me by surprise like everyone else here, and I think Superpig's suggestion is really the easiest and most elegant way to handle this. Thanks by the way :)
     
    superpig likes this.
  48. DMGregory

    DMGregory

    Joined:
    Jul 31, 2013
    Posts:
    4
    I stumbled across this today too. Here's a little method I cobbled together to help fix-up prefab references for instances that you drag into the scene.

    It lets me get more or less my original intention of "an object that remembers what prefab it was spawned from" in a self-contained way where I don't have to remember to wire up that reference for every copy I place in my scene, or store that information in an external spawning script. It does require some discipline for copies spawned at runtime to use its Spawn method instead of directly Instantiating it though.

    Code (csharp):
    1. public class Respawnable : MonoBehaviour {
    2.     public Respawnable sourcePrefab;
    3.  
    4.     // Call Spawn methods on this object instead of Instantiate directly,
    5.     // so that it can fix up the spawned prefab reference for you.
    6.     public GameObject Spawn() {
    7.         var spawned = Instantiate(sourcePrefab);
    8.         spawned.sourcePrefab = sourcePrefab;
    9.         return spawned.gameObject;
    10.     }
    11.     public GameObject Spawn(Vector3 position, Quaternion orientation) {
    12.         var spawned = Spawn().transform;
    13.         spawned.localPosition = position;
    14.         spawned.localRotation = orientation;
    15.         return spawned.gameObject;
    16.     }
    17.  
    18.     // In the editor, fix up prefab reference when making a prefab
    19.     // or adding the prefab into a scene.
    20. #if UNITY_EDITOR
    21.     private void OnValidate() {    
    22.         if(sourcePrefab == this || sourcePrefab == null) {
    23.             var prefab = (Respawnable)
    24.                        UnityEditor.PrefabUtility.GetPrefabParent(this);
    25.             sourcePrefab = prefab ?? this;
    26.             Debug.Log(gameObject.name + " prefab fixed up");
    27.         }
    28.     }
    29. #endif
    30. }
     
    Last edited: Mar 6, 2018
  49. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,073
    Stumbled upon this problem today.
    I don't know how there can be no clean solution for this BUG (by design, whatever).

    How can this be that two prefabs can reference each-other and cannot reference self.
    If they want to keep current state than introduce new serializable class that will hold unchangable reference or Attribute that will prevent unity to change the damn reference to prefab during instantiation.
     
  50. Crashhelmet

    Crashhelmet

    Joined:
    Mar 13, 2018
    Posts:
    5
    Hi guys,

    Stumbled on this issue today, too. My workaround is to create a ScriptableObject that holds the prefab reference. Reference this SO in the prefab and get the reference in Awake.

    SO
    Code (CSharp):
    1. [CreateAssetMenu(menuName = "Prefab Reference")]
    2. public class PrefabReference : ScriptableObject
    3. {
    4.     public GameObject prefab;
    5. }
    Prefab
    Code (CSharp):
    1. public PrefabReference prefabReference;
    2.  
    3. private GameObject prefab;
    4.  
    5. private void Awake()
    6. {
    7.     prefab = prefabReference.prefab;
    8. }
     
    Omti1990, WildStyle69 and phobos2077 like this.