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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Why is my script instantiating a (clone)(clone) from a prefab

Discussion in 'Scripting' started by juiced12, Feb 2, 2021.

  1. juiced12

    juiced12

    Joined:
    Nov 8, 2018
    Posts:
    44
    I have a spawnable component on a gameobject. Now the idea is that this object can spawn clones of itself but I don't want it to spawn a clone of the clone. I want it to spawn from the original prefab.

    My code looks like this

    Code (CSharp):
    1. public class Spawner : MonoBehaviour
    2. {
    3.     public Spawner spawner
    4.     // Start is called before the first frame update
    5.     void Start()
    6.     {
    7.      
    8.     }
    9.     // Update is called once per frame
    10.     void Update()
    11.     {
    12.         if (Input.GetKeyDown(KeyCode.E))
    13.         {
    14.             Instantiate(spawner.gameObject, Vector3.zero, Quaternion.identity);
    15.         }
    16.     }
    17. }
    On runtime I spawn a new Spawner gameobject which is a clone of the prefab. This prefab can then spawn more prefabs by pressing E. However it isn't cloning the prefab. It appears to be cloning a clone of it

    Can anyone tell me why this is happening?

    I'm instantiating from an original prefab, surely it would just be a clone of the prefab, ie Spawner(Clone)

    I'm getting Spawner(Clone)(Clone)
     
  2. Ray_Sovranti

    Ray_Sovranti

    Joined:
    Oct 28, 2020
    Posts:
    172
    It sounds like you're having an issue with the way that an instantiated prefab updates its references. If you have any reference from one component in a prefab pointing to another component on the same prefab, then when instantiating, the reference will point to that component on the instantiated copy. This is nearly universally the right decision; could you imagine the chaos if every reference on an instantiated copy pointed to the prefab instead of itself? Even the simplest things like a reference to a renderer would be utterly broken.

    Unfortunately though, there is no way to tell Unity "When you instantiate this, please don't update this reference, I know it's unusual but that's what I want this time." You'll have to work around it by holding that reference somewhere else where it won't be modified. One way to do this would be to have the instantiated copy use the same reference that was used to instantiate it in the first place - e.g. it finds the prefab reference on the manager that spawned it, for example.
     
    Bunny83 likes this.
  3. juiced12

    juiced12

    Joined:
    Nov 8, 2018
    Posts:
    44
    @Ray_Sovranti Thanks for the detailed response Ray. This makes a lot of sense and I'll implement your workaround.

    The reason that this is an issue in the first place, is because the actual gameobject has a bunch of other scripts on it and when the clone of clone is instantiated it calls the start methods on all these components and it's causing a bunch of errors in the console as if the instantiated object has already called the start method.

    I add a line renderer in one of the components start methods and it appears that it's already added at the point of it being cloned. I guess I expected it to be spawning as a completely fresh object.
     
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,539
    I would also recommend to store the prefab reference somewhere else.

    However if you really need the instance to have a reference to his own prefab, this is possible but requires some additional work. First of all when you instantiate the prefab in code, you just have to make sure you set that reference again to the actual prefab on the newly instantiated object. If you have instantiated your prefab in the editor by dragging the prefab into the scene, you would need to manually assign the actual prefab reference there. From that point on you shouldn't have any issues as long as you always re-set the prefab variable in newly instantiated objects.

    Since this approach is quite error prone as you can easily forget to assign the reference in the scene or you forget to copy the reference when you call Instantiate, storing the prefab reference elsewhere is the better approach. You could even use a ScriptableObject like this:

    Code (CSharp):
    1. [CreateAssetMenu]
    2. public PrefabRef : ScriptableObject
    3. {
    4.     public Transform prefab;
    5. }
    So for every prefab you can create another instance of that scriptable object in your project. The script on the prefab would simply reference this PrefabRef instance and the prefab variable in that asset could simply reference the prefab. So they are referencing each other. References to other assets that are not part of the prefab will stay.
     
  5. juiced12

    juiced12

    Joined:
    Nov 8, 2018
    Posts:
    44
    Thanks for your input Bunny.

    I've actually decided to create a PrefabManager class which will return all the different prefabs I want depending on their ID. I'll make it a singleton aswell to make it readily available.

    I'm going to have to take a good look at scriptable objects because I see them being mentioned all the time but have never actually used them.
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,539
    Yes, reducing the prefab to an ID is also quite handy if you need saving / loading at some point since referencing assets from runtime serialized data is kinda tricky.
     
    Risible_Kitty likes this.