1
\$\begingroup\$

I have been using Unity a while and always Instantiated objects slightly differently from what many of the tutorials and posts online say (I've seen 3 very similar ways). It seems to have the same outcome much of the time, however now I am trying to instantiate the prefab using my "Resources" folder instead of dragging it from the inspector and on one method it said it was an 'object' not 'gameobject'. I can't really find anything on Unity website or here, so what is the difference between the following:

 // These two lines to me seem identical, but most tutorials dont cast, and rather use the latter of the two instead, why?
 currentBall = (GameObject)Instantiate(Resources.Load(prefabName, typeof(GameObject)));
 currentBall = Instantiate(Resources.Load(prefabName, typeof(GameObject))) as GameObject;

  // This one I know is different somehow, because Unity said cannot convert from Object to GameObject, but honestly i do not understand why
  currentBall = GameObject.Instantiate(Resources.Load(prefabName, typeof(GameObject)));
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

The short answer is that it's all about type safety, and how we navigate type guarantees.

Casting like this:

(TypeIWant)SomeMethodThatReturnsAMoreGeneralType()

will throw an error if the value returned from the method isn't convertible to the TypeIWant.

But using the as keyword:

SomeMethodThatReturnsAMoreGeneralType() as TypeIWant

will instead populate the value with null if the conversion fails. So if there's a legitimate reason you might get a different return type for your particular application, using as lets you detect and handle that case gracefully, without throwing errors and try/catch complications.

There's no significant performance difference between the two in modern C#, so the main function here is to clearly show your intent and trap errors where you want them. If your game absolutely expects TypeIWant here, then use a cast. If that assumption is ever violated, you'll get the error immediately and can go investigate right at the source.

On the other hand, as shows that you knowingly expect the type to sometimes be something else, and that this is not immediate cause for concern. It will be the job of the subsequent code to check for nulls where there shouldn't be nulls and fire a corresponding error if they need to.

The last case is due to the fact that even if we write GameObject.Instantiate, it's still calling UnityEngine.Object.Instantiate, which is made for cloning anything that derives from UnityEngine.Object - that means it can't guarantee you're going to get a GameObject out at the end. GameObject.Instantiate(GetComponent<Renderer>().sharedMaterial) is also valid syntactically, returning a Material. So we need a cast or as to convert the return value to a GameObject before we can store it in a variable with type GameObject.

Of course you could also skip the cast entirely, and use the generic form or Instantiate/Load if you know in advance what type you want. That way a mismatch is a compile-time error, rather than a runtime error, and can often be faster to catch & correct:

TypeIWant myThing = Instantiate<TypeIWant>(Resources.Load<TypeIWant>(path));

Wherever possible, I like to type my variables according to the most specific type I need from them. So rather than storing a bullet prefab as a GameObject or Transform, I'll give it a Projectile component and expose a slot for it like:

public Projectile bulletPrefab;

Then I can instantiate it with:

 var bullet = Instantiate<Projectile>(bulletPrefab);

Now I have strong type guarantees throughout, without casts, and the Inspector will enforce that whatever I assign to bulletPrefab has the required component. (Don't worry, Instantiate will still clone the whole prefab, game object, other scripts, child hierarchy, and all - not just a floating Projectile component)

\$\endgroup\$
4
  • \$\begingroup\$ Oh, nice about the diamond method. I think I was vaguely aware that those methods existed, but I should start using them more. \$\endgroup\$ Commented Dec 5, 2017 at 3:42
  • \$\begingroup\$ And I used to Instantiate a bulletPrefab as a Bullet (which is a component of the bulletPrefab, however I found that I was often having to write bullet.gameObject.transform..etc...etc instead of just bullet.transform, so I prefer to to bullet.GetComponent<Bullet>() now. (I'm probably doing wrong here but it is the way i learnt most comfortably.) Again tho, thanks for your detailed and easy to understand explanations @DMGregory \$\endgroup\$ Commented Dec 5, 2017 at 15:23
  • 1
    \$\begingroup\$ bullet.transform is very likely more efficient than bulletTransform.GetComponent<Bullet>(). Or you could consider giving Bullet its own methods to handle any firing/positioning so nobody else needs to touch its transform at all. ;) \$\endgroup\$
    – DMGregory
    Commented Dec 5, 2017 at 15:37
  • \$\begingroup\$ Thanks again mate. No wonder why I suffer with bad frame rate on phone games even when very little is happening. I will try to change my practises from now on \$\endgroup\$ Commented Dec 5, 2017 at 16:26

You must log in to answer this question.

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