How to instantiate a prefab?

​Hi,

I tryed a alternative solution for the pinSet instantiation and that was use Resource.Load (“path”) to instantiate the pin set from prefab without creat a public variable.

But it didn’t work and I don’t know why. Here is the code:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using AOT;
using System.Diagnostics;

public class PinSetter : MonoBehaviour
{
private int lastStandingCount;
private float lastStandingTime;
private bool ballInTheBox;
private GameObject pinCountUI;
private Ball ball;
privateGameObject pinSet;

// Use this for initialization
void Start ()
{
lastStandingCount = -1;
pinCountUI = GameObject.Find (“PinCountUI”);
ballInTheBox = false;
ball = GameObject.FindObjectOfType ();

  // Also "Pins" here
         pinSet = Resource.Load ("Prefabs/Pins") as GameObject; 

}

// Update is called once per frame
void Update ()
{
if (ballInTheBox) {
CountStanding ();
CheckStanding ();
}
}

//Count standing pins
public int CountStanding ()
{
int pinCounter = 0;

  foreach (Pin pin in GameObject.FindObjectsOfType<Pin>()) {
  	if (pin.IsStanding ()) {
  		pinCounter++;
  	}
  }
  	
  pinCountUI.GetComponent<Text> ().text = pinCounter.ToString ("D2");
  return pinCounter;

}

//Check if the ball is the pinsetter box
void OnTriggerEnter (Collider collider)
{
if (collider.GetComponent ()) {
pinCountUI.GetComponent ().color = Color.red;
ballInTheBox = true;
}
}

//Destroy pins when they exit pinsetter
void OnTriggerExit (Collider collider)
{
if (collider.GetComponentInParent ()) {
DestroyObject (collider.transform.parent.gameObject);
}
}

void CheckStanding ()
{
	int count = CountStanding ();
	if (count != lastStandingCount) {
		lastStandingTime = Time.time;
		lastStandingCount = CountStanding ();
		return;
	}

	if (Time.time - lastStandingTime > 5) {
		PinsHaveSettled ();
	}

	Invoke ("PinsHaveSettled", 3);
}

void PinsHaveSettled ()
{
	lastStandingCount = -1;
	ballInTheBox = false;
	pinCountUI.GetComponent<Text> ().color = Color.green;
	ball.Reset ();
}

public void RaisePins ()
{
	foreach (Pin pin in GameObject.FindObjectsOfType<Pin>()) {
		pin.Raise ();

	}
}

public void LowerPins ()
{
	foreach (Pin pin in GameObject.FindObjectsOfType<Pin>()) {
		pin.Lower ();
	}

}

public void RenewPins ()
{
	ball.Reset ();
	Instantiate (pinSet, new Vector3 (0, 0, 1828), Quaternion.identity);
}

}

There’re a couple of things which need to be addressed to fix your problem.

First one: probably, you haven’t created a Resource folder inside the Assets and put the Pins prefab inside.
You need to do that, because the Resource.Load method searches for assets only inside the Resource folder. So, create that folder if you haven’t yet done it and put the Pins prefab inside.

Once you’ve done that… the code won’t work the same. :smiley:

This is because in Start() you passed the reference of the Pins prefab to the pinSet variable, but you didn’t actually told the code to create an instance of it. So, the code needs to be like this:

pinSet = Resource.Load ("Pins") as GameObject; Instantiate(pinSet, new Vector3 (0, 0, 1828), Quaternion.identity);
Of course, if you put a Prefabs folder inside the Resource one, and the former contains the Pins prefab, you need to write “Prefabs/Pins” instead of “Pins”. And clearly you can call the RenewPins() from Start(), instead of the Instantiate line.

This will work without problems, with one downside thou: the code will generate up to 11 Pins(Clone) instances during the game, and you won’t be able to destroy the previous ones because Unity doesn’t allow, for security reasons, the destroying of Assets (and even if you could, with this code you’d end up with no prefab to instantiate after the first frame).

If you want to know how to do this, I can explain it in another post if you’re interested. :wink:

1 Like

Thanks. This was really helpfull. Let me just recap a few details:

  1. I can “load” a prefab into a empty game object (pinSet in this case).

  2. I can create an instance of this game object (pinSet);

Now what I couldn’t quite get:

  1. I can’t destroy the instance? Or I can’t destroy the game object to which I loaded the resource?
1 Like
  1. pinSet is a variable, of type GameObject. With the Resource.Load() you assign a reference to pinSet, pointing to the object defined as an argument in the Resource.Load() method. You’re not “loading” anything.
    Think of it as “Hello, I’m pinSet, and I contain the address of the object Resources/Prefabs/Pins, should you ever want to reach it for any reason”.

  2. When you assign the reference to pinSet, you can use it to do a number of things, among them creating an instance of such reference using the method Instantiate(). It’s basic good practice to never instantiate an object without having declared before a reference. Let me elaborate on this.

You can, if you want, instantiate a new set of pins without using any reference, with this line of code:

Instantiate(Resources.Load("Prefabs/pins"));

At runtime, Unity will create a Pins(Clone) game object in the hierarchy, but you won’t be able to access it since you haven’t assigned a reference to it before.
In general programming, this means that you won’t be able to perform any kind of operation on it, since you won’t be able to access it in any way (i.e.: no reference), and you can’t assign a reference to a previously instantiated object which had no reference already.
In Unity, you can however create a reference to an instance after it is created, because any new game object instantiated at runtime inherits the Object class of Unity, so you can access it by using a Find(), for example with this code:

Instantiate(Resources.Load("Prefabs/pins"));
pinSet = GameObject.Find("Pins(Clone)");

Note that, with these two lines of code, pinSet doesn’t have a reference to the prefab, but to the game object which was instantiated a line before.

And this brings us to the Destroy matter.
With the last two lines of code in Start(), you can then write in RenewPins():

if (pinSet) Destroy (pinSet);
Instantiate(Resources.Load("Prefabs/pins"));
pinSet = GameObject.Find("Pins(Clone)");

This code will 1) destroy the Pins(Clone) game object from the Hierarchy previously created in Start() and 2) create a new Pins(Clone) game object in the Hierarchy.

If, instead, you assigned to pinSet the reference to the prefab directly, you won’t be able to destroy the Pins(Clones) game objects, since using Destroy() will give you this error:

Destroying assets is not permitted to avoid data loss.
If you really want to remove an asset use DestroyImmediate (theObject, true);

And if you try to use DestroyImmediate(), you’ll get this error:
Destroying GameObjects immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.

Thus, apparently, creating an instance and then declaring a variable and giving it a reference seems the best way to go. But it’s not, there’s a third way which is way more efficient, since it allows you to completely avoid the use of Find(), which is a slow method.

private GameObject pinSetPrefab, pinSetInstance;
void Start () {
    pinSetPrefab = Resources.Load("Prefabs/Pins") as GameObject;
    RenewPins();
}

public void RenewPins(){
    if (pinSetInstance) Destroy (pinSetInstance);
    pinSetInstance = Instantiate (pinSetPrefab);
}

Notice that, now, pinSetPrefab just stores the reference to the Pins prefab, whereas pinSetInstance stores a reference to the instantiated Pins(Clone), and you managed the same result as before but with additional advantages, namely:

  1. You can perform any operation on the Pins(Clone) object, since you can access the pinSetInstance variable which always holds a reference to it;
  2. You won’t need to use any Find(), FindGameObjectWithTag(), etc. which are very slow in execution.

Now, for the final nail on the coffin: don’t be afraid to use public references instead of private ones, if the private ones specifically need a string to be assigned. When accessing objects via strings (name, tag, etc.), it can be a pain in the ass in the future if, for any reason, you’ll go and change the name, tag, position of the object in the assets, etc. When you assign the reference to a public variable via the Inspector, instead, you protect yourself from such problems in the future.
And if you despise that much seeing public variables exposed in the Inspector, you can just assign the reference to it once, and then add [HideInIspector] just before the declaration of the public variable you want to hide, the reference will remain but you won’t see it anymore in the Inspector.

That’s pretty much it, I hope I shed some light on your doubts.

3 Likes

This was so clarifing.

Thanks a lot!

1 Like

Privacy & Terms