13
\$\begingroup\$

i am instantiating a few game objects in my scene using prefabs and i would like to attach a script to some of them to do something at a certain time but for that i need to reference to scene objects and its not allowing me. anyone know why? and whats the workaround?

i can pull those prefabs into my scene and use them as game objects but that would destroy the idea of prefabs isn't?

\$\endgroup\$
1
  • 2
    \$\begingroup\$ I don't think I have the facility to post an answer to the quality level I would like, but you would not save a link to the scene objects in the prefab. You would use script to create the links dynamically. There are several ways to do this; all very simple, and convenient in different ways. You could also set this up to work when you hit play and run the script in-game or at the instant where you drop a copy of the prefab into your scene. I will post an answer, when I can, if others are not satisfactory. \$\endgroup\$
    – Gnemlock
    Commented Jan 3, 2017 at 0:31

5 Answers 5

25
\$\begingroup\$

In the editor, prefabs can only reference other prefabs. Prefabs are supposed to be shared between scenes. That means they can't rely on the object being present in every scene.

If you need them to reference something in the scene, you need to get that reference at runtime. There are several ways to do that.

  • Attach the reference to the object which instantiates the prefabs and have the spawning script set it on every object it instantiates:

    GameObject bullet = Instantiate(bulletPrefab, transform.position, transform.rotation); bullet.GetComponent<BulletScript>().shooter = gameObject;

  • Search for the game object in the Start method of your prefab.

  • Store the reference to the object in a global variable (a public static variable of an appropriate class) so all the instantiated prefabs can access it.
\$\endgroup\$
3
  • 1
    \$\begingroup\$ I believe finding with tag is fast, but I could be mistaken \$\endgroup\$
    – Evorlor
    Commented Jan 3, 2017 at 13:28
  • 2
    \$\begingroup\$ @Evorlor That depends on how many objects you have with the same tag. \$\endgroup\$
    – Philipp
    Commented Jan 3, 2017 at 14:25
  • 2
    \$\begingroup\$ It tends to be faster because you have less objects in the pool to search through. However, it will be as slow as Find if most of your objects have the tag. \$\endgroup\$ Commented Oct 17, 2018 at 20:55
4
\$\begingroup\$

Unity does not allow you to reference scene objects to prefabs. It allows any other reference. It's because prefabs have a template-like nature and thus are unaware of the scene.

There are many workarounds for this case, most of them involve a static variable.

  1. Assign a Tag to your scene object and then find it by searching through objects

    var all = FindObjectsOfType<GameObject>();
    foreach ( var item in all ) { 
        if (item.Tag.CompareTo("MyTag") == 0) 
            return item;
    }
    //or
    var item = GameObject.FindWithTag("MyTag");
    
  2. Add a script to your scene object and store the reference there

    internal GameObject MyRef;
    void Awake() { MyRef = this; }
    
  3. Add an empty game object (reference manager) in your scene with a script to store all references

    //assign these in inspector
    public GameObject MyRef1;
    public GameObject MyRef2;
    

Note that

in case 2,3 you may use static variables or singleton pattern.

e.g.

case 2:

public class SceneObject1 : MonoBehaviour
{
    public static GameObject MyRef { get; private set; }
    void Awake() { MyRef = this; }
}

case 3:

public class RefManager : MonoBehaviour
{
    static RefManager instance;
    void Awake() { instance = this; }

    public GameObject MyRef1;

    //use this in prefab script
    public static GameObject MyRef1 { get { return instance.MyRef1; } }
}

//in prefab
var inPrefabVariable = RefManager.MyRef1;
\$\endgroup\$
1
\$\begingroup\$

You should definitively read the Unity manual about prefabs

The prefab acts as a template from which you can create new object instances in the scene.

The purpose of prefabs is to be instantiated into the scene.

When in the Project tab, it acts as a "regular file" and must be independant from any object in any scene (but it can have references from other prefabs also in the Project tab) so as to be reusable in any scene.

The advantage of prefabs is, when you edit the values of one of the and hit "Apply", all the instances in the other scenes will have the new values. (Except if you have already overriden the values of the instance)

You have to instantiate the prefab into your scene so as to be able to link objects of the scene into your prefab. The instantiation and creation of the links to the prefab instance can be done using the inspector, or, at runtime, using code.

\$\endgroup\$
4
  • \$\begingroup\$ yes but whats the work around? \$\endgroup\$ Commented Jan 3, 2017 at 6:56
  • \$\begingroup\$ The only "workaround" is to instantiate the prefab and create the links by hand (either through code or with drag & drop) \$\endgroup\$
    – Hellium
    Commented Jan 3, 2017 at 7:09
  • 3
    \$\begingroup\$ This seems to miss the question entirely; the user is not asking us to explain prefabs. They are asking why it can't store game object references. In actual fact, it can. While the user is assumingly trying to create a hard coded reference, we can still make references and set them up dynamically. You seem to mention this in a comment, but I'm not sure how useful this answer would be to users not knowing how to set that up. \$\endgroup\$
    – Gnemlock
    Commented Jan 3, 2017 at 8:28
  • \$\begingroup\$ By explaining what a prefab is, the asker can see that it's not possible to store a reference of a scene's object into prefab. You can add references to an instance of a prefab, but not a prefab itself. \$\endgroup\$
    – Hellium
    Commented Jan 3, 2017 at 8:35
1
\$\begingroup\$

I think there is a way around this. If I am getting your question right, you want your prefab's public variables to have objects reference from the scene. What you can do is make prefab instances particularly for that scene. Which means

  1. you can drag and drop you prefab in hierarchy.
  2. Unpack the Prefab object in hierarchy
  3. Now attach the references of other object to the script of Unpacked Prefab.
  4. Disable the Unpacked Prefab in hierarchy.
  5. Now where ever you have code to instantiate the prefab originally, replace it with this Unpacked Prefab object from hierarchy.
  6. Set Active(true) the instantiated object.

What basically we are doing here is instead of using prefab we are using object as a reference for instantiating objects. And since the object are is disabled in hierarchy it is not working but holding all the property of its prefab, just like the prefab itself.

Note while instantiating the object, don't forget to SetActive true. Here is the example how to do it :

GameObject cloneObject = Instantiate(Object);
cloneObject.SetActive(true);
\$\endgroup\$
0
\$\begingroup\$

One workaround is to note the instanceID of any scene object references, they are merely ints, and will save to the prefab just fine. In order to restore missing object references in new instances of the prefab, write a script that searches for GameObjects whose instanceID match with the ones we stored in the prefab. To get a full list of all available GameObjects in all open scenes, you can use eg GameObject.FindObjectsOfType(typeof(GameObject));

I'm not suggesting this is a robust solution, or a good idea, just that it is possible to deal with this kind of scenario.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .