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.

Prefab with reference to itself...??

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

  1. yoonitee

    yoonitee

    Joined:
    Jun 27, 2013
    Posts:
    2,337
    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,525
    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,337
    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,525
    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,821
    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. }
     
  10. runevision

    runevision

    Joined:
    Nov 28, 2007
    Posts:
    1,853
    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:
    962
    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,853
    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:
    1,852
    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:
    19
    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:
    1,665
    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:
    19
    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,821
    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:
    887
    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:
    158
    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:
    887
    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:
    13
    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:
    17
    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
     
  25. Maisey

    Maisey

    Joined:
    Feb 17, 2014
    Posts:
    290
    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.
     
unityunity