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

Prefab with reference to itself...??

Discussion in 'Scripting' started by yoonitee, Jun 20, 2016.

  1. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,362
    I have a prefab with a reference to itself in a component:

    GameObject myPrefab;

    When I instantiate this prefab, the myPrefab on the instance points to the instance instead of the prefab.

    Is this expected behavior????

    How can I make the reference always point to the prefab and not the instance?
     
  2. SubZeroGaming

    SubZeroGaming

    Joined:
    Mar 4, 2013
    Posts:
    1,008
    public GameObject myPrefab;

    GameObject go = (GameObject)Instantiate(myPrefab);
     
  3. SubZeroGaming

    SubZeroGaming

    Joined:
    Mar 4, 2013
    Posts:
    1,008

    go will hold the prefab created and you can manipulate the values.
     
  4. bigmisterb

    bigmisterb

    Joined:
    Nov 6, 2010
    Posts:
    4,221
    OK, after reading, and testing. (kind of important)

    If you reference a prefab onto its self, and you instantiate it, it will reference the new game object instead of the prefab.

    To fix this, we use Resources.Load(prefabname)

    Try this:
    Create a new folder called Resources in your Assets. Put your prefab into that folder. Create two scripts.
    Code (csharp):
    1.  
    2. // the first is a controller which will spawn your prefab
    3. using UnityEngine;
    4. using System.Collections;
    5.  
    6. public class MyInstantiatorController : MonoBehaviour {
    7.     public GameObject prefab;
    8.     // Use this for initialization
    9.     void Start () {
    10.         Instantiate(prefab);
    11.     }
    12. }
    13.  
    Code (csharp):
    1.  
    2. // next is the script on the object in question
    3. using UnityEngine;
    4. using System;
    5. using System.Collections;
    6.  
    7. public class MyPrefabController : MonoBehaviour {
    8.     public GameObject prefab;
    9.  
    10.     public string prefabName = "MyPrefab";
    11.  
    12.     // Use this for initialization
    13.     void Start () {
    14.         prefab = (GameObject)Resources.Load(prefabName);
    15.         Debug.Log(prefab);
    16.     }
    17. }
    18.  
    now, create a prefab in the Resources folder and call it "MyPrefab"
    Create a cube in the world, which is the prefab object, and attach the "MyPrefabController" to it.
    Set the name of the prefabName as the same name of the prefab you created.
    Drag that cube to the MyPrefab object you created.

    Now, create an empty object and assign the "MyInstantiatorController" to it.

    Run the game, and your console, should now display the name of the prefab in the folder, and not the object.
     
    yoonitee likes this.
  5. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    This might actually be a bug. Prefabs referencing other prefabs isn't a new concept and it seems odd that the behavior would differ depending on *what* prefab you reference. Part of me wonders if they're trying to avoid a serialization loop there or something.

    That being said - this is sort of a weird pattern. If you could describe *why* you're trying to do this then we might be able to suggest a better way to do it.
     
  6. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,362
    Thanks. Well I was doing this to create some fractals. So a prefab would make smaller versions of itself.

    Anyway, I made a fix by testing if the script is referencing a parent object, and if so changing it to reference the prefab.

    I think if you have a script on a prefab that holds a reference to itself. It must store this just as "SELF". Can't quite see the reason for this!

    Why I would do such a thing is that I can use the script to reference other prefabs but to make something fractionally it has to reference its own prefab.

    I suppose I could use Resources.Load but would that be slower? Does it get resources off the hard disk?

    Apparently this is "by design". But still wasted about 5 hours on it!
     
  7. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    The first load gets it from an asset file on disk. Subsequent calls for the same thing return a cached version.

    I'd still investigate the route of having a "fractal manager" that holds all the types of fractals and manages the creation of smaller ones. Saves you from trying to manage things via strings - which is never really a good idea.
     
  8. augheyj

    augheyj

    Joined:
    Oct 24, 2016
    Posts:
    5
    FYI, this behavior is still an issue. I ran into this issue today in the exact same way. I have a respawn component that respawns itself when it is destroyed.
     
  9. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Code (csharp):
    1. public GameObject SuperCoolPrefab;
    2.  
    3. public GameObject InstantiateSuperCoolPrefab()
    4. {
    5.     GameObject returnObject = Instantiate(SuperCoolPrefab);
    6.     returnObject.GetComponent<ReferenceToOwnPrefabOnThisScript>().PrefabReference = SuperCoolPrefab;
    7.     return returnObject;
    8. }
     
    Bunny83, ThisIsNik and Kaldrin like this.
  10. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,877
    A Prefab Asset referencing an object within the Prefab Asset itself is treated as an internal reference and will be remapped to remain an internal reference when the Prefab Asset is instantiated as a Prefab instance. So the Prefab instance will now reference an object in the Prefab instance (still a self reference). This has always been the case and is by design. It's very commonly used when a component needs access to some other components on the same Prefab and this is setup via serialized references rather than the script finding the other components in Awake or Start or similar.
     
    Joe-Censored, Bunny83 and Pacosmico like this.
  11. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    974
    How to access a prefab from the instance of the prefab then? Is there a way?

    I explain my problem related to this on this thread: https://forum.unity.com/threads/reset-prefabs-when-entering-a-room.791679/#post-5267277
     
    EZaca likes this.
  12. ARGB32

    ARGB32

    Joined:
    Feb 20, 2013
    Posts:
    15
    Any known bug where this would stop working? (2019.3.5f1)

    It looks like sometimes unity will link a field in a component on the prefab back to the Prefab asset in the project and not the instance in the scene.

    I can mess the with prefab directly in a text editor and get it to switch which of the components point to the asset and which points to the instance. For identical components.

    The upshot is there's no apparent reason why sometimes it might pick the asset over instance. It's not limited to nested prefabs, it happens when it's just the prefab in the scene.
    When playing the game it will bake it down to the instance correctly only 'sometimes' too but that looks to be a different issue.

    The only way to make sure it doesn't happen is to apply the setting as an override value and keep the override (don't apply to the underlying prefab). This is a pretty critical bug with prefabs in 2019. And again I want to stress this isn't limited to nested prefabs.
     

    Attached Files:

  13. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,877
    I replied in the thread you started here: https://forum.unity.com/threads/bug...ference-the-prefab-asset.850537/#post-5611300
     
    Bunny83 likes this.
  14. Krzysztof_Poros

    Krzysztof_Poros

    Joined:
    Feb 13, 2020
    Posts:
    4
    I wanted to instantiate car parts on a car, depening on how tuned that car is.
    I wanted to store current car configuration in an SO, so i can easly transfer it between scenes, and later, easly plug it into saving.
    I cannot store a scene reference, instance of car part into SO as i get type mismatch.
    I cannot store original prefab of car part in car part itself, as it references itself after instancing.

    So i have to make a workaround SO of original car parts to reference prefabs in assets. Nice complication. :D
     
  15. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,525
    That's not necessary when you just update your prefab reference variable when you instantiate your prefab. So assume you have a MonoBehaviour called CarPart on each of your prefab. You can simply add this one line after you instantiated the prefab:

    Code (CSharp):
    1. public CarPart somePrefabReference;
    2.  
    3. CarPart newPart = Instantiated(somePrefabReference);
    4. newPart.prefab = somePrefabReference;
    Of course an even cleaner solution would be to add a construction method to your CarPart class so you can store the prefab reference even in a private variable.

    Code (CSharp):
    1. // inside the CarPart class;
    2. public CarPart prefab;
    3.  
    4. public CarPart Create()
    5. {
    6.     var instance = Instantiate(this);
    7.     instance.prefab = this;
    8.     return CarPart;
    9. }
    10.  
    Of course this would only work when Create is used on the actual prefab reference. When used on an instance, the prefab field would be set to that instance which wouldn't make any sense. If you actually set up the prefab self reference in the inspector, you could change the line to
    instance.prefab = this.prefab;


    This assumes that the prefab and all instances do have their "prefab" field "fixed" so it always points to the actual prefab. So you could even call this method on an existing instance and it would just reuse it's prefab field. Be warned that objects instantiated in the editor by dragging them into the scene of course will not be "fixed". (Actually "unfixed" since Unity fixes self references after instantiating them so we want to revert that ^^). So when you drag a prefab into the scene you would need to manually replace the prefab reference into its "prefab" variable. Though from that point on you can use Create and always have the right prefab reference at hand.


    Note that while you can store references to prefabs in an SO asset, it would not really work when you use the JsonUtility to serialize the SO content. So if you want a save system at runtime, you have to have some more primitive way of referencing a certain prefab. A common solution is to simply setup an array of all carparts and simply store the index into that array alongside. So when reconstructing the object you can simply lookup the prefab based on that index / ID.
     
  16. bloodthirst69

    bloodthirst69

    Joined:
    Oct 1, 2017
    Posts:
    28
    Hey , i am bit late to the thread but i had this issue too where i wanted to store a reference to the "clean version" of the prefab instead of self referencing , so what i did is instead of creating a field that directly contains the ref to the prefab , i made a ScriptableObject that holds a reference to the prefab's asset , Unity's serializer doesn't do the reference checking on those and takes them as they are.

    just to make things easier , here's the scriptable object code :

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. namespace Bloodthirst.Core.BISDSystem
    4. {
    5.     [CreateAssetMenu(menuName = "BISD/Prefab Reference/Data")]
    6.     public class PrefabReferenceData : ScriptableObject
    7.     {
    8.         [SerializeField]
    9.         private GameObject prefabReference;
    10.  
    11.         public GameObject PrefabReference => prefabReference;
    12.     }
    13. }
    14.  
    examples of use :

    Code (CSharp):
    1. public class EntityIdentifier : MonoBehaviour
    2.     {
    3.         [SerializeField]
    4.         private int id;
    5.  
    6.         public int Id
    7.         {
    8.             get { return id; }
    9.             set { id = value; }
    10.         }
    11.  
    12.         [SerializeField]
    13.         private PrefabReferenceData prefabReferenceData;
    14.  
    15.         /// <summary>
    16.         /// Prefab used to spawn this entity
    17.         /// </summary>
    18.         public PrefabReferenceData PrefabReferenceData => prefabReferenceData;
    19.  
    20.         private void Start()
    21.         {
    22.                 // this will be reference to the prefab's asset
    23.                 prefabReferenceData.PrefabReference;
    24.         }
    25. }
    26. }
     
  17. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,008
    Are you going to create scriptable object for every prefab ?
    I hope it will work for you but this solution is completely unacceptable.

    Anyway I have just dropped in to this topic to once again show discontent with the current state of things.
    A more than a decade long BUG by design.
     
  18. bloodthirst69

    bloodthirst69

    Joined:
    Oct 1, 2017
    Posts:
    28
    In my case it worked well with the addition of an editor script that automates a lot of stuff, but i can see how this isn't really doable for big projects, i haven't tried this but i think you can work around the issue if u implement the deserialization callback and create a custom attribute to put on fields that you want to reset their value to the original prefab instead of keeping the copied reference , which is again , isn't ideal since it requires inheriting from monobehaviour.
    one solution i would suggest for unity to implement is to add an attribute called [KeepPrefabRef] or something similar that does this automatically since they have access to the whole prefab/serialization backend.
     
    Flavelius likes this.
  19. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    This sounds like a pretty complicated solution to avoid a single GetComponent call after instantiating a prefab.
     
    KelsoMRK likes this.
  20. Flavelius

    Flavelius

    Joined:
    Jul 8, 2012
    Posts:
    926
    The getcomponent solution doesn't work if the prefab is an instance in the scene already. I want to use this for recursive UI panels. When the serialized field reference is drag-drop assigned in prefab editing mode, single-clicking it pings the correct asset in the project window, but clicking the field in the scene instance pings itself in the hierarchy. It's confusing and not helpful.
    My current workaround is creating a prefab variant, but this is not ideal, as those get 'baked' out to full hierarchies (here a duplicate) in builds.
    I think the attribute idea is a great solution.
     
    EZaca likes this.
  21. huulong

    huulong

    Joined:
    Jul 1, 2013
    Posts:
    223
    I just had this issue today and I was about to send a bug report. It was obscure and quite hard to identify the cause, as I was instantiating that prefab many times, and counting on the name of the prefab reference to find the appropriate asset in Resources. You could say defining a game object reference just to get a name string is convoluted, but it's really to prevent mistakes (picking a prefab ref is safer than typing a name manually and risking a typo).

    The prefab was named Rock, and it was meant to break into other Rock prefabs that I would instantiate. But the instances of the main Rock were named something like "Rock(Clone) 0". When trying to find the prefab in Resources, I got an error as " 'Rock(Clone) 0' cannot be found. " After testing a bit, I realized my Prefab ref kept getting reverted to the "Self" Prefab Root reference.

    What bothers me is the fact that the interface says nothing about this, and the reference is silently reverted to Self. Also, there are two ways to set a game object reference and in this case both lead to the same result:

    1. Either you select a Scene reference, which, in prefab edit mode, means you pick the prefab root, or some child on the prefab itself. In this case I would definitely expect any instance to hold a reference to the corresponding object on its own instance

    2. Or you select an Asset reference, which is unrelated to your object, and doesn't change even if you instantiate the prefab. In this case I expect the reference to stand still.

    To me, the point of having Scene vs Asset selection is to distinguish those two cases. Not only for the prefab root, but for any prefab that happens to be in the scene. If you exit Prefab edit mode, you'll have even more Scene objects to reference, and it's crucial to be able to tell if you're pointing at a scene object or a prefab.

    But when it comes to the prefab root, both approaches do the same thing, there is no warning, you cannot see the blue prefab icon turn into the gray scene object icon until you leave and re-enter Prefab edit mode; and if you just exit Prefab edit mode and check your prefab instance, you'll see the blue Prefab icon and think everything is all right; except it's actually pointing to your Prefab instance which happens to be blue like the Prefab asset, and there's no visual distinction until you click on it and you see it highlights the scene object.

    Fortunately I didn't mean to have a recursive behaviour and I'm gonna switch to a different prefab soon (the Rock should break into smaller Rocks, so not the same prefab), but other developers may want to count on that recursion depending on their projects.

    I also thought about a few other workarounds:

    a. I can also switch back to name-based Resource access, but it will only work for devs actually putting their prefab into a Resources folder, and not afraid of having the mentioned typo/prefab rename issues, and possible Resources loading overhead.

    b. I can create a Prefab Variant that is just like the original, yet considered like a different object. Unfortunately, this solution only works for 1 level: if I break my Rock Variant, it will try to spawn its own Rock Variant instances, and those being the same, they will refer to the Self root, causing the original issue again...
     
  22. Flavelius

    Flavelius

    Joined:
    Jul 8, 2012
    Posts:
    926
    Another workaround is to use a scriptableObject as a delegate instead of the prefab itself as reference. This still polutes the project with extra assets though.
     
  23. ttesla

    ttesla

    Joined:
    Feb 23, 2015
    Posts:
    16
    Yes this is annoying but that's how it is designed and we have to live with it.
    There is a very simple solution though; make a factory class and instantiate your prefab from there. This way you can also pool the prefab. You may pre generate many instances and give the next one when requested.
     
  24. theCodeHermit

    theCodeHermit

    Joined:
    Sep 20, 2018
    Posts:
    39
    I finally found an acceptable (for me) workaround. Provided you already have the prefab existing in the scene already, this works great.

    Code (CSharp):
    1.  
    2.         //Get path to nearest (in case of nested) prefab from this gameObject in the scene
    3.          string prefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(gameObject);
    4.  
    5.          //Get prefab object from path
    6.          Object prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(Object));
    7.  
    8.          //Instantiate the prefab in the scene, as a sibling of current gameObject
    9.          PrefabUtility.InstantiatePrefab(prefab, transform.parent);
    10.  
    Original answer from IvovdMarel at:
    https://answers.unity.com/questions/21731/editor-script-instantiating-prefab-and-maintaining.html
     
    a52, halley and Merivo like this.
  25. Maisey

    Maisey

    Joined:
    Feb 17, 2014
    Posts:
    300
    Just encountered this myself. Quite amazing that this is still an (active) issue :/ I would guess there is some usage for the behavior, but it's definitely not expected.
     
    taedal5 likes this.
  26. taedal5

    taedal5

    Joined:
    Feb 2, 2021
    Posts:
    11
    This is a really awkward and hard to figure out behaviour which feels like a bug to me. You can get around it by having another class instatiate the prefab, but when you don't know about this behaviour (and why should you assume this strange behaviour?) it can waste hours.
     
    Last edited: Jun 27, 2022
  27. D853211

    D853211

    Joined:
    Oct 28, 2022
    Posts:
    1
    There goes half a day of my life.
    It take years of changing keywords to even find this post and learn that there is no fixing it be cause it is "as intended"

    In what case would someone decide to drag and drop a self reference when you can just call gameObject in the scripts?

    I was intending on making a gameObject sorting method base on the source prefab, now I geuss I have to make a error prone, complicated vision of it instead.
     
    jaschott and BollyFlex like this.
  28. Merivo

    Merivo

    Joined:
    May 19, 2022
    Posts:
    2
    While this self-referencing exception may be 'intended', the problem is it's a major gotcha. I don't care what the reason is, Unity is meant to be easy to use, so it shouldn't be this way.

    That being said, if you're not keen on theCodeHermit's work-around above, my own work-around is to duplicate the prefab and have them reference each other. The main benefit of this is it takes 5 seconds and doesn't require modifying the script at all. The downside is you have 2 prefabs to update if you ever need to modify it.

    I think it's important to provide multiple different solutions, since whichever is best really depends on what you're doing with it.
     
  29. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,525
    That's just kinda ignorant because this feature actually has nothing to do with prefabs in particular but with the way how cloning / instantiating works and how it fixes self references after the cloning. This fixing is intended and most would expect this to happen for most internal references. Like referencing a collider or other script on a deeply nested object within a prefab / object. Not fixing those references would make the whole concept of prefabs pretty pointless.

    I do agree that it may seem a bit counter intuitive when you first discover this behaviour.

    Easy is a quite relative term. Any kind of software wants to be easy and intuitive, however there are always compromises you have to take. The issue is just your own lack of knowledge. Things are never naturally intuitive. It's always based on what your pre-knowledge and experience is.

    I'd like you to think about the actual mechanic and propose how it should behave and how it should differentiate between what kind of references should be treated as internal and which one shouldn't. How do you decide that? Unity fixes any references that is on the cloned object or any nested object but keeps any references to things outside the thing that is cloned. It makes perfect sense and is the only reasonable approach.

    A common approach is to use another mediator asset like a scriptable object that holds the prefab reference. The prefab itself would reference the mediator object and when you want to instantiate that prefab, you grab the prefab reference from the mediator object. Since the mediator is a seperate external asset, cloning the prefab would preserve the reference to the mediator. That's usually the best and error-proof approach. Fixing the references after instantiate does work, but if there's one error, the reference would be lost for good. Anyways, there are very rare cases where a prefab would actually require a reference to itself. Keep in mind that the whole prefab concept is an editor only feature to manage pre made instances. At runtime a prefab is just an instance that lives outside the scene and can be cloned into the scene. Instances that have been cloned at runtime do not know from what they have been cloned. Conceptionally there's no difference between a clone from another object in the scene or from a prefab object. The concept of a "prefab instance" only exists in the editor for authoring and scene editing purposes. At runtime you can not figure out if an object is an instance of a particular prefab as there is no such information.
     
  30. Noros

    Noros

    Joined:
    Jun 15, 2017
    Posts:
    1
    I just wish there was a clear way to tell when a prefab is switching a project asset reference to a self reference. Got stuck on this today.

    Ended up using the scriptable object workaround for my case (an object that the player can "pickup" and "drop"), so that the reference to the prefab asset could be preserved as the object was destroyed and instantiated.
     
  31. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,525
    There is. Any reference to objects that directly belong to the source object would point to the instantiated version after instantiation. Just what you would expect. Of course it affects only references of objects that are actually instantiated. So separate assets (Mesh, Material, etc) are not cloned as they are not part of the gameobject hierarchy that make up the gameobject.
     
  32. Slink0

    Slink0

    Joined:
    Dec 26, 2020
    Posts:
    1
    I've encountered this behavior of self-referencing when I would prefer an absolute reference to the prefab in question, but there are also plenty of cases in which the self-reference (intended behavior) is extremely useful. Is there some reason Unity could not include a way to indicate an absolute prefab reference, akin to an absolute cell reference in MS Excel being denoted by a $, for example?

    It seems like a common enough issue to be worth considering something like this, especially given the input from some others in the thread about how current workarounds are a royal pain in larger projects that rely on the absolute references heavily. Plus, even if there is always a workaround, it would sure be nice to not need one to accomplish this.

    If this comment would be better submitted to some sort of feedback thread let me know, I'm new to the community and don't really know how this works.
     
    a52 likes this.
  33. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,863
    An absolute prefab reference is an asset path. See post #24 in this thread.
     
    a52 likes this.
  34. a52

    a52

    Joined:
    Mar 15, 2018
    Posts:
    16
    Here are a set of scripts I made for objects to remember links to prefabs, including when the prefab is themself. They seem to work fine in most cases. Hopefully these should be useful to others!

    https://github.com/skylord-a52/Unity-Remember-Self-Prefab/tree/main

    Edit: Thank you to Bunny83 and Halley for pointing out some errors that would have prevented this from building. It now works properly (as far as I can tell) in built games as well as the editor.
     
    Last edited: Sep 13, 2023
  35. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,525
    That doesn't make much sense since your script relies on the UnityEditor namespace which you can not use in a built game. You use editor only classes (like the AssetDatabase) in a runtime script. That means you can not build your game.
     
    a52, SisusCo and halley like this.
  36. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,863
    During game design time, your script could use Editor validation functions and automatically keep a string which is the asset path, or even just the original Prefab Asset (a GameObject not present in a scene). At runtime, even in a build, the asset path can be used to find the Preset Asset so you can instantiate it.

    Which is what we were already saying in #24 and #33 in this thread.

    As Bunny83 said, there is no UnityEditor (e.g., PrefabUtility) functionality in a built Unity executable, so those functions can only be used inside an Editor folder, or inside an
    #if UNITY_EDITOR ... #endif
    directive block, if you want to be able to build.
     
    a52 likes this.
  37. a52

    a52

    Joined:
    Mar 15, 2018
    Posts:
    16
    Ah. I didn't realize there were scripts that worked in Play mode but not in a build -- I had assumed that if it worked in one it would work in the other, and that "Editor-only" meant Edit mode only. I'm fairly new to actually building games (I have a horrible track record on completing projects!), so I'm not always aware of the restrictions.

    This was exactly my goal, actually, to serialize the asset path in Edit mode and then retrieve the asset from the path during runtime. If you can't use AssetDatabase, how would you do that? With Asset Bundles?

    Edit: Yep, that or Resources.Load. I'll modify my script.
     
    Last edited: Sep 13, 2023