- Home /
Random: NullReferenceException: Object reference not set to an instance of an object
I'm working on a shoot em up game. And sometimes when my enemies die the error appears in the console. I Instantiate random enemy prefabs. All with the right tag, every variable is set in the inspector. The error appears on like 5 - 10% of killed enemies. It was working the whole time.
NullReferenceException: Object reference not set to an instance of an object
EnemyController.TakeDamage (Single damage) (at Assets/Scripts/EnemyController.cs:98)
UnityEngine.Component:SendMessage(String, Object)
Shot:OnTriggerEnter2D(Collider2D) (at Assets/Scripts/Shot.cs:28)
Shot.cs:28+
void OnTriggerEnter2D(Collider2D col)
{
if(col.tag == "player" && this.gameObject.tag == "enemyShot")
{
col.SendMessage("TakeDamage", damage);
Destroy(this.gameObject);
}
if(col.tag == "enemy" && this.gameObject.tag == "playerShot")
{
col.SendMessage("TakeDamage", damage); //28
Destroy(this.gameObject);
}
}
EnemyController.cs
protected void Die()
{
Punktanzeige.SendMessage("AddPoints", ScorePoints);
Instantiate(explosion, this.gameObject.transform.position, this.gameObject.transform.rotation);
this.GetComponent<DropController>().SpawnPowerUp();
speaker.PlayOneShot(destroySound);
Destroy(this.gameObject);
}
protected void TakeDamage(float damage)
{
speaker.PlayOneShot(hitSound); //98
sr.sprite = hitSprite;
nextSprite = Time.time + hitEffectTime;
hp -= damage;
if (hp <= 0)
{
Die();
}
}
Tell us more about "speaker" as it seems that this object is empty at line 98. Where is it initialized and where is it destroyed?
It's not the speaker if I remove it the error appears on the next line. speaker, sr, are all initialized in Start() of the EnemyController.cs.
Another question: how many colliders are attached to each enemy?
Hmm. Okay, if I may suggest a test: remove everything from TakeDamage() except Die() – so that every hit will cause a kill. See if the incidence spikes?
Answer by crisis-sheep · Jul 12, 2017 at 01:30 PM
I'm gonna go with a race condition on this one. Collision callbacks have caused some sticky situations for me in the past.
I think on occasion there are multiple shots that would cause the enemy to destroy itself. One gets there first and the code drops out from under its feet (so to speak) in the middle of execution. That's why the null reference doesn't go away when you remove what appears to be the offending line – the whole object is now null.
Have you considered using an object pool? It's a better option usually than instantiation/destroying. Instead of destroying the enemy, disable it – with gameObject.SetActive(false);
– and return it to the pool.
I'm pretty sure you won't run into any null references if you do this.
Never tried object pooling but I think this should help. Weird that it worked the whole time.
I hear you! It's the main gotcha with concurrency. There are so many paths for the code to take (even in simple cases), and often most of them are valid, but there are certain outliers that will bite you.
PS: Have you migrated to the new version of Unity? Possibly some underlying change that exposed the issue?
Update: I created a objectPooler for Shots and Enemies. Replaced:
if(col.tag == "enemy" && this.gameObject.tag == "playerShot")
{
col.Send$$anonymous$$essage("TakeDamage", damage);
Destroy(this.gameObject);
with:
if(col.tag == "enemy" && gameObject.tag == "playerShot")
{
col.gameObject.GetComponent<EnemyController>().TakeDamage(damage);
gameObject.SetActive(false);
}
It does still happen but much less. I'm using Unity 5.5.4f1
Answer by Cornelis-de-Jager · Jul 13, 2017 at 09:46 PM
I think the problem is that you aren't calling the behaviour. Since you are calling snedMessage on the collider it won't work. Since the collider doesn't have a function called TakeDamage. Try replacing send message with the following:
void OnTriggerEnter2D(Collider2D col)
{
if(col.tag == "player" && this.gameObject.tag == "enemyShot")
{
col.GetComponent<EnemyController>().TakeDamage(damage);
Destroy(this.gameObject);
}
if(col.tag == "enemy" && this.gameObject.tag == "playerShot")
{
col.GetComponent<EnemyController>().TakeDamage(damage);
Destroy(this.gameObject);
}
}