Reference to prefab changing to clone self-reference

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,

1 Like

?

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.

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.

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.

using UnityEngine;


public class TestInstanceButton : MonoBehaviour
{
    public Transform prefab; 


    protected void OnGUI()
    {
        float height = 30;
        float width = 70;

        // Create a new rect
        Rect rect = new Rect();
        rect.height = height;
        rect.width = width;

        // Put the rect over the transform
        Vector3 pos = UI.cam.WorldToScreenPoint(transform.position);
        pos.y = Screen.height - pos.y;

        // Multiply by half so the rect is centered
        rect.x = pos.x - (width * 0.5f);
        rect.y = pos.y - (height * 0.5f);
        
        // Create the GUI
        GUILayout.BeginArea(rect);

        if (GUILayout.Button("test", GUILayout.Height(height)))
        {
            Transform instance = (Transform)Instantiate(this.prefab);

            if (!instance)
            {
                Debug.LogError("Unable to create instance.");
                return;
            }

            // Move the instance over to the side a little so we can see it
            Vector3 new_pos = new Vector3();
            new_pos.x = transform.position.x + 3;
            new_pos.y = transform.position.y;
            new_pos.z = transform.position.z;
            instance.position = new_pos;
        }

        GUILayout.EndArea();
    }

}

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?

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.

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).

Make sure you’re selecting a Prefab from the Project and not from the Scene.

Hope this helps.

@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,

4 Likes

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. :?

A simple “break prefab reference at startup” or “don’t make the assumption that I meant your own object” method would solve this.

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).

I was aware of Resources.Load, but I didn’t want to muck with a resources folder :stuck_out_tongue:

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

2 Likes

Submitted. UT, please fix this asap!

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.

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.

2 Likes

(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.

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.

1 Like

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!

1 Like

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.