UnassignedReferenceException whey I kill an enemy which has shot a laser

Hi! Not sure if this question really goes here. I discovered the bug now, but I think it might be related to a previous lesson.

Whenever I kill an enemy which has previously shot a laser, I get this error multiple times (like 30 or 40 per enemy):

UnassignedReferenceException: The variable enemyLaserPrefab of Enemy has not been assigned.
You probably need to assign the enemyLaserPrefab variable of the Enemy script in the inspector.

Though the laser prefab is assigned to the enemies prefabs. It must have something to do with the instanced enemies being destroyed. I don’t get the error if I kill an enemy before it shoots. I’ve compared my Enemy.cs script to the one in this lesson and I can’t see any differences other than variable names (oneEnemyLaser instead of projectile, and so on).

I’m pretty mystified about the nature of these errors. Any insight?

Thanks!

Could you take a screenshot of the error, and maybe include your code as well?

It’s possible that the destruction of the object tried to access one of the spawned lasers, after that laser was destroyed ( at least that’s what I’d guess), but hard to say without seeing the code and where exactly the error is pointing.

Which…have you checked at which line of your code the error is pointing in the console?

Here’s an image of the error, and the code. The error points to the line where the enemyLaserPrefab object is instantiated (line 57 of my code). Weirdly, I changed the order of lines 50 and 51 (the ones inside if (shotCounter <= 0f) { } ), and that reduced the number of errors per enemy killed from around 35 to 1 or 2.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour
{


[Header("Enemy")]
[SerializeField] float health = 100;
[SerializeField] float shotCounter;
[SerializeField] float minTimeBtwShots = .2f;
[SerializeField] float maxTimeBtwShots = 3f;


[Header("Laser")]
[SerializeField] GameObject enemyLaserPrefab;
[SerializeField] float xLaserVel = 0f;
[SerializeField] float yLaserVel = -8f;


[Header("SFX")]
[SerializeField] AudioClip enemyExplosionSFX;
[SerializeField] [Range(0,1)] float enemyExplosionVolume = 0.75f;
[SerializeField] AudioClip enemyLaserSFX;
[SerializeField] [Range(0, 1)] float enemyLaserVolume = 0.75f;

[Header("VFX")]
[SerializeField] GameObject explosionStarsPrefab;
[SerializeField] float durationOfExplosion = 2f;


// Start is called before the first frame update
void Start()
{
    shotCounter = Random.Range(minTimeBtwShots, maxTimeBtwShots);
}

// Update is called once per frame
void Update()
{
    CountDownAndShoot();
}

private void CountDownAndShoot()
{
    shotCounter -= Time.deltaTime;
    if (shotCounter <= 0f)
    {
        shotCounter = Random.Range(minTimeBtwShots, maxTimeBtwShots);    // Placing this line here (instead of in line 51) highly reduces the number of errors per enemy killed (?)
        EnemyShot();
    }
}

private void EnemyShot()
{
    GameObject oneEnemyLaser = Instantiate(enemyLaserPrefab, transform.position, Quaternion.identity) as GameObject;
    oneEnemyLaser.GetComponent<Rigidbody2D>().velocity = new Vector2(xLaserVel, yLaserVel);
    AudioSource.PlayClipAtPoint(enemyLaserSFX, Camera.main.transform.position, enemyLaserVolume);    
}

private void OnTriggerEnter2D(Collider2D other)
{
    DamageDealer damageDealer = other.gameObject.GetComponent<DamageDealer>();
    if (!damageDealer) { return; }      //if damageDealer doesn't exist, exit the process
    ProcessHit(damageDealer);
}

private void ProcessHit(DamageDealer damageDealer)
{
    health -= damageDealer.GetDamage();
    damageDealer.Hit();
    if (health <= 0)
    {
        ProcessEnemyDeath();
    }
}

private void ProcessEnemyDeath()
{
    Destroy(gameObject);
    GameObject oneExplosion = Instantiate(explosionStarsPrefab, transform.position, transform.rotation);
    Destroy(oneExplosion, durationOfExplosion);
    AudioSource.PlayClipAtPoint(enemyExplosionSFX, Camera.main.transform.position, enemyExplosionVolume);
}

}

Hi Ruben,
NullReferenceException means that a reference (“link”) to an instance is missing. Double click on the error message to see to which line in your code it is referring. If you exposed a field in the Inspector, make sure that it’s not empty.

Don’t assign a laser from the Hierarchy to your enemy because that laser could get destroyed, and the enemy could not shoot anymore.

The error points to the line where the enemyLaserPrefab object is instantiated (line 57 of my code).

I made sure the laser assigned to the prefab enemies is the prefab EnemyLaser. Still not sure what’s happening.

I’ll keep on digging…

Further info: if I comment the instantiation of the laser (and the line where I give it velocity), I get errors with the audio (PlayClipAtPoint). Note that this error only appears if I kill an enemy, not before.

UnassignedReferenceException: The variable enemyLaserSFX of Enemy has not been assigned.
You probably need to assign the enemyLaserSFX variable of the Enemy script in the inspector.
UnityEngine.AudioSource.PlayClipAtPoint (UnityEngine.AudioClip clip, UnityEngine.Vector3 position, System.Single volume) (at C:/buildslave/unity/build/artifacts/generated/bindings_old/common/Audio/AudioBindings.gen.cs:866)
Enemy.EnemyShot () (at Assets/Scripts/Enemy.cs:59)
Enemy.CountDownAndShoot () (at Assets/Scripts/Enemy.cs:51)
Enemy.Update () (at Assets/Scripts/Enemy.cs:42)

Could you please share a screenshot of your enemy prefab? The referenced objects must be inside your Assets folder, not in the Hierarchy.

The other two enemy prefabs have the exact same fields but for the sprite in the sprite renderer, the Explosion Stars Prefab (different colours) and the Offset of the Circle Collider 2D. The referenced EnemyLaser is in the Prefabs folder. The referenced audios are in SFX folder, and the ExplosionParticles(Colour) are in the VFS folder.

  1. Remove the references from the prefab and re-add them to ensure that Unity really got the prefabs, not game objects from the Hierarchy.
  2. Check the value of enemyLaserPrefab in the Start method of the Enemy object.
  3. Look for Destroy methods in your code and make sure that no prefab gets destroyed.
  1. I checked that all references were prefabs.
  2. The Start method of Enemy.cs has no enemyLaserPrefab, but I checked the correct value in the inspector and that it is correctly instantiated and the instance is used from then on.
  3. Destroy methods in all code files only has as argument instances or the gameObject.

Still getting the errors, but the game runs (both in Windows and web platforms), so I’ll keep on going.

I’m afraid looking at the Inspector is not sufficient here. You must check the value of enemyLaserPrefab during runtime, and you could do that in Start. If your fields in the Inspector contain a reference, that’s good but we do not know what is going on during runtime. Maybe the reference gets lost at some point. We need to figure out when and where that happens.

  1. Destroy methods in all code files only has as argument instances or the gameObject .

What do you mean by “argument instances”?