- Home /
Argument Exception: The Object you want to instantiate is null (A new one?)
Hi, I've looked at the other ones previously asked, but this one has me stumped.
I'm trying to simply spawn a DeathExplosion object based on a prefab when this enemy ship dies. I've used this logic elsewhere, and it works fine, but in this case, it doesn't.
I have attached a DeathExplosion prefab in the Inspector, so that wouldn't seem to be the problem. I also have the prefab Instantiating before the parent object is destroyed, so I wouldn't think that is the issue.
If you can spot something blatantly obvious that I am not seeing, or suggest other avenues to explore, that would be hugely appreciated!!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour {
// Config
[SerializeField] int myMaxHullStrength = 100;
[SerializeField] int myHullStrength = 100;
[SerializeField] DeathExplosion myDeathExplosion = null;
// Cached references
// States
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (myHullStrength == 0) {
SpawnExplosion (transform.position);
Destroy (gameObject);
}
}
public void TakeDamage(int damage, bool ignoreShield) {
if (shieldDown () || ignoreShield) {
myHullStrength = Mathf.Clamp (myHullStrength - damage, 0, myMaxHullStrength);
}
}
private bool shieldDown (){
EnemyShield myShield = null;
if (myShield = GetComponentInChildren <EnemyShield>()) {
return false;
} else {
return true;
}
}
private void SpawnExplosion (Vector2 startPosition){
DeathExplosion newDeathExplosion;
newDeathExplosion = Instantiate (myDeathExplosion);
newDeathExplosion.transform.position = startPosition;
newDeathExplosion.transform.SetParent (null);
}
}
To avoid multiple checks on the logics in the Update, if your variable "myHullStrength" is just reduced by the method "TakeDamage" (and it seems to be). I'd rather change the name of Update to CheckDeath and call it from TakeDamage. You don't need to keep checking it each frame.
About the explosion, I'd remove the spawnExplosion from the Enemy class to an external one and pass the enemy's position for the explosion to be spawned.
And a third thing I'd do is an object pool for explosions to avoid multiple possible instantiations during the gameplay :)
I'm not sure what exactly is happening but maybe the prefab reference is just not available anymore by the time instantiate is finished, as your object with the ref must be already destroyed.
Have you tried to put a simple Debug.Log(myDeathExplosion);
before calling Instantiate
?
Answer by Murgs001 · May 20, 2018 at 11:52 AM
Sorry folks, but it seems I had a severe case of LateNight-itis....
Somewhere along the way during my duplication of the prefab based on another existing prefab, I must have hit Apply at a bad time, and it seems it had replicated the 'Enemy' script component. As a result, one of them (the one I was staring at) looked fully fine, whereas a duplicate component was lurking in the corner with a null field, giggling quietly at me.
I'll just shuffle away and enjoy my piece of humble pie right now while it's still warm. :P
Thanks again for the advice!!
Answer by jonnydredd · May 20, 2018 at 07:32 AM
Hello, it seems you are doing a few extra things that maybe you don't need to. Is the Death Explosion just a GameObject? If so, give this a try:
replace [SerializeField] DeathExplosion myDeathExplosion = null;
with public GameObject myDeathExplosion;
and be sure to assign that in the inspector.
then replace your SpawnExplosion function with this
private void SpawnExplosion (Vector2 startPosition){
GameObject newDeathExplosion = Instantiate (myDeathExplosion);
newDeathExplosion.transform.position = startPosition;
}
You may also be able to use the following instead
private void SpawnExplosion(Vector2 startPosition)
{
GameObject newDeathExplosion = Instantiate(myDeathExplosion, startPosition, Quaternion.identity);
}
[SerializeField] DeathExplosion myDeathExplosion = null;
Is perfectly fine, and should be used ins$$anonymous$$d of public GameObject myDeathExplosion;
to preserve encapsulation
And, instantiating the object, and then, setting its position is also totally fine.
Hi! Thanks for your comments. Unfortunately I've also tried making it public ins$$anonymous$$d of SerializedField just in case, but it makes no difference.
Based on some diagnostic debug.logs, it seems myDeathExplosion is null immediately before Instantiate is called. However I can't see how that would happen, as the Inspector shows the correct prefab, and I never (purposely?) reassign that field in code.
Is there a trick/quirk that I am missing?
Thanks again!
Here is another observation: I have successfully used identical code for another gameobject to trigger an explosion upon Destroy(). The only difference that I can see is that in the successful case, the gameobject was instantiated at runtime, whereas this gameobject exists upon level creation.
In the failed case (object being part of the level), the assignment of a prefab in the a Serializefield seemed completely ignored (already null even in Start() ).
Is this just one of those things you gotta do a different way?
Can you provide the code of DeathExplosion
? Does the prefab you drag & drop in the myDeathExplosion
is in the scene, or is a prefab in the Project
tab? I guess DeathExplosion
is a script destroying itself after a given time.
If the DeathExplosion
you provide is in the scene when you hit start, the object destroys itself automatically (I guess). So you have to provide a prefab asset (from the Project
tab)