Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Bug Null reference error when enemies reached the exit point

Discussion in 'Scripting' started by cdfertolo, Mar 16, 2023.

  1. cdfertolo

    cdfertolo

    Joined:
    Feb 23, 2023
    Posts:
    2
    Hi guys,
    I'm new here, I'm trying to learn unity coding, I have started working on a tower defense game that I found on github.
    I'm trying to modify it step by step and learn how the codes works but I get an error everytime I start the game and an enemies reach the final point.

    That's the error
    NullReferenceException: Object reference not set to an instance of an object
    Enemy.OnTriggerEnter2D (UnityEngine.Collider2D collider2D) (at Assets/Scripts/Enemy.cs:73)

    I tried to understand why it is null and I think it depends from the way the GameManager was called in the enemy script but I can't find how to fix it.

    That's the Enemy script:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Enemy : MonoBehaviour {
    6.     //SerializeField - Allows Inspector to get access to private fields.
    7.     //If we want to get access to this from another class, we'll just need to make public getters
    8.     [SerializeField]
    9.     private Transform exitPoint;
    10.     [SerializeField]
    11.     private Transform[] wayPoints;
    12.     [SerializeField]
    13.     private float navigationUpdate;
    14.     [SerializeField]
    15.     private int healthPoints;
    16.     [SerializeField]
    17.     private int rewardAmount;
    18.  
    19.     private int target = 0;
    20.     private Transform enemy;
    21.     private Collider2D enemyCollider;
    22.     private Animator anim;
    23.     private float navigationTime = 0;
    24.     private bool isDead = false;
    25.  
    26.     public bool IsDead
    27.     {
    28.         get { return isDead; }
    29.     }
    30.  
    31.     // Use this for initialization
    32.     void Start () {
    33.         enemy = GetComponent<Transform>();
    34.         enemyCollider = GetComponent<Collider2D>();
    35.         anim = GetComponent<Animator>();
    36.         Debug.Log("Starting register");
    37.         GameManager.Instance.RegisterEnemy(this);
    38.         Debug.Log("Registered");
    39.     }
    40.    
    41.     // Update is called once per frame
    42.     void Update () {
    43.         if (wayPoints != null && !isDead)
    44.         {
    45.             //Lets use change how fast the update occurs
    46.             navigationTime += Time.deltaTime;
    47.             if(navigationTime > navigationUpdate)
    48.             {
    49.                 //If enemy is not at the last wayPoint, keep moving towards the wayPoint
    50.                 //otherwise move to the exitPoint
    51.                 if(target < wayPoints.Length)
    52.                 {
    53.                     enemy.position = Vector2.MoveTowards(enemy.position, wayPoints[target].position, navigationTime);
    54.                 }
    55.                 else
    56.                 {
    57.                     enemy.position = Vector2.MoveTowards(enemy.position, exitPoint.position, navigationTime);
    58.                 }
    59.                 navigationTime = 0;
    60.             }
    61.         }
    62.     }
    63.  
    64.     //If we trigger the collider2D.tag for checkpoints for finish.
    65.     //If it hits the checkpoints, increase the index and move to the next checkpoint
    66.     //otherwise enemy is at the finish line and should be destroyed.
    67.     void OnTriggerEnter2D(Collider2D collider2D)
    68.     {
    69.         if (collider2D.tag == "checkpoint")
    70.             target += 1;
    71.         else if (collider2D.tag == "Finish")
    72.         {
    73.             GameManager.Instance.RoundEscaped += 1;
    74.             GameManager.Instance.TotalEscape += 1;
    75.             GameManager.Instance.UnregisterEnemy(this);
    76.             GameManager.Instance.isWaveOver();
    77.         }
    78.         else if(collider2D.tag == "projectile")
    79.         {
    80.             Projectile newP = collider2D.gameObject.GetComponent<Projectile>();
    81.             enemyHit(newP.AttackStrength);
    82.             Destroy(collider2D.gameObject);
    83.         }
    84.     }
    85.     public void enemyHit(int hitPoints)
    86.     {
    87.         if(healthPoints - hitPoints > 0)
    88.         {
    89.             healthPoints -= hitPoints;
    90.             anim.Play("Hurt");
    91.             GameManager.Instance.AudioSource.PlayOneShot(SoundManager.Instance.Hit);
    92.         }
    93.         else
    94.         {
    95.             anim.SetTrigger("didDie");
    96.             die();
    97.         }
    98.     }
    99.  
    100.     public void die()
    101.     {
    102.         isDead = true;
    103.         enemyCollider.enabled = false;
    104.         GameManager.Instance.TotalKilled += 1;
    105.         GameManager.Instance.AudioSource.PlayOneShot(SoundManager.Instance.Death);
    106.         GameManager.Instance.AddMoney(rewardAmount);
    107.         GameManager.Instance.isWaveOver();
    108.     }
    109. }
    All the things in this condition else if (collider2D.tag == "Finish") (line 71) seems to doesn't work properly.

    That's the GameManager script

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5.  
    6. public enum gameStatus
    7. {
    8.     next, play, gameover, win
    9. }
    10. public class GameManager : Singleton<GameManager> {
    11.     //SerializeField - Allows Inspector to get access to private fields.
    12.     //If we want to get access to this from another class, we'll just need to make public getters
    13.     [SerializeField]
    14.     private int totalWaves = 10;
    15.     [SerializeField]
    16.     private Text totalMoneyLabel;   //Refers to money label at upper left corner
    17.     [SerializeField]
    18.     private Text currentWaveLabel;
    19.     [SerializeField]
    20.     private Text totalEscapedLabel;
    21.     [SerializeField]
    22.     private GameObject spawnPoint;
    23.     [SerializeField]
    24.     private Enemy[] enemies;
    25.     [SerializeField]
    26.     private int totalEnemies = 3;
    27.     [SerializeField]
    28.     private int enemiesPerSpawn;
    29.     [SerializeField]
    30.     private Text playButtonLabel;
    31.     [SerializeField]
    32.     private Button playButton;
    33.  
    34.     private int waveNumber = 0;
    35.     private int totalMoney = 10;
    36.     private int totalEscaped = 0;
    37.     private int roundEscaped = 0;
    38.     private int totalKilled = 0;
    39.     private int whichEnemiesToSpawn = 0;
    40.     private int enemiesToSpawn = 0;
    41.     private gameStatus currentState = gameStatus.play;
    42.     private AudioSource audioSource;
    43.  
    44.     public List<Enemy> EnemyList = new List<Enemy>();
    45.     const float spawnDelay = 2f; //Spawn Delay in seconds
    46.  
    47.     public int TotalMoney
    48.     {
    49.         get { return totalMoney; }
    50.         set
    51.         {
    52.             totalMoney = value;
    53.             totalMoneyLabel.text = totalMoney.ToString();
    54.         }
    55.     }
    56.  
    57.     public int TotalEscape
    58.     {
    59.         get { return totalEscaped; }
    60.         set { totalEscaped = value; }
    61.     }
    62.     public int RoundEscaped
    63.     {
    64.         get { return roundEscaped; }
    65.         set { roundEscaped = value; }
    66.     }
    67.     public int TotalKilled
    68.     {
    69.         get { return totalKilled; }
    70.         set { totalKilled = value; }
    71.     }
    72.  
    73.     public AudioSource AudioSource
    74.     {
    75.         get { return audioSource; }
    76.     }
    77.    
    78.     // Use this for initialization
    79.     void Start () {
    80.         playButton.gameObject.SetActive(false);
    81.         audioSource = GetComponent<AudioSource>();
    82.         ShowMenu();
    83.     }
    84.    
    85.     // Update is called once per frame
    86.     void Update () {
    87.         handleEscape();
    88.     }
    89.  
    90.     //This will spawn enemies, wait for the given spawnDelay then call itself again to spawn another enemy
    91.     IEnumerator spawn()
    92.     {
    93.         if (enemiesPerSpawn > 0 && EnemyList.Count < totalEnemies)
    94.         {
    95.             for (int i = 0; i < enemiesPerSpawn; i++)
    96.             {
    97.                 if (EnemyList.Count < totalEnemies)
    98.                 {
    99.                     Enemy newEnemy = Instantiate(enemies[Random.Range(0, enemiesToSpawn)]);
    100.                     newEnemy.transform.position = spawnPoint.transform.position;
    101.                 }
    102.             }
    103.             yield return new WaitForSeconds(spawnDelay);
    104.             StartCoroutine(spawn());
    105.         }
    106.     }
    107.  
    108.     ///Register - when enemy spawns
    109.     public void RegisterEnemy(Enemy enemy)
    110.     {
    111.         EnemyList.Add(enemy);
    112.     }
    113.     ///Unregister - When they escape the screen
    114.     public void UnregisterEnemy(Enemy enemy)
    115.     {
    116.         EnemyList.Remove(enemy);
    117.         Destroy(enemy.gameObject);
    118.     }
    119.     ///Destroy - At the end of the wave
    120.     public void DestroyAllEnemies()
    121.     {
    122.         foreach(Enemy enemy in EnemyList)
    123.         {
    124.             Destroy(enemy.gameObject);
    125.         }
    126.         EnemyList.Clear();
    127.     }
    128.  
    129.     public void AddMoney(int amount)
    130.     {
    131.         TotalMoney += amount;
    132.     }
    133.  
    134.     public void SubtractMoney(int amount)
    135.     {
    136.         TotalMoney -= amount;
    137.     }
    138.  
    139.     public void isWaveOver()
    140.     {
    141.         totalEscapedLabel.text = "Escaped " + TotalEscape + "/10";
    142.         if (RoundEscaped + TotalKilled == totalEnemies)
    143.         {
    144.             if(waveNumber <= enemies.Length)
    145.             {
    146.                 enemiesToSpawn = waveNumber;
    147.             }
    148.             setCurrentGameState();
    149.             ShowMenu();
    150.         }
    151.     }
    152.  
    153.     public void setCurrentGameState()
    154.     {
    155.         if(totalEscaped >= 10)
    156.         {
    157.             currentState = gameStatus.gameover;
    158.         }
    159.         else if(waveNumber == 0 && (TotalKilled + RoundEscaped) == 0)
    160.         {
    161.             currentState = gameStatus.play;
    162.         }
    163.         else if(waveNumber >= totalWaves)
    164.         {
    165.             currentState = gameStatus.win;
    166.         }
    167.         else
    168.         {
    169.             currentState = gameStatus.next;
    170.         }
    171.     }
    172.  
    173.     public void ShowMenu()
    174.     {
    175.         switch (currentState)
    176.         {
    177.             case gameStatus.gameover:
    178.                 playButtonLabel.text = "Play Again!";
    179.                 AudioSource.PlayOneShot(SoundManager.Instance.Gameover);
    180.                 break;
    181.             case gameStatus.next:
    182.                 playButtonLabel.text = "Next Wave";
    183.                 break;
    184.             case gameStatus.play:
    185.                 playButtonLabel.text = "Play";
    186.                 break;
    187.             case gameStatus.win:
    188.                 playButtonLabel.text = "Play";
    189.                 break;
    190.         }
    191.         playButton.gameObject.SetActive(true);
    192.     }
    193.     public void playButtonPressed()
    194.     {
    195.         Debug.Log("Play Button Pressed");
    196.         switch (currentState)
    197.         {
    198.             case gameStatus.next:
    199.                 waveNumber += 1;
    200.                 totalEnemies += waveNumber;
    201.                 break;
    202.             default:
    203.                 totalEnemies = 3;
    204.                 totalEscaped = 0;
    205.                 TotalMoney = 10;
    206.                 TowerManager.Instance.DestroyAllTower();
    207.                 TowerManager.Instance.RenameTagsBuildSites();
    208.                 totalMoneyLabel.text = TotalMoney.ToString();
    209.                 totalEscapedLabel.text = "Escaped " + totalEscaped + "/10";
    210.                 AudioSource.PlayOneShot(SoundManager.Instance.NewGame);
    211.                 break;
    212.         }
    213.         DestroyAllEnemies();
    214.         TotalKilled = 0;
    215.         RoundEscaped = 0;
    216.         currentWaveLabel.text = "Wave " + (waveNumber + 1);
    217.         StartCoroutine(spawn());
    218.         playButton.gameObject.SetActive(false);
    219.     }
    220.     private void handleEscape()
    221.     {
    222.         if (Input.GetKeyDown(KeyCode.Escape))
    223.         {
    224.             TowerManager.Instance.disableDragSprite();
    225.             TowerManager.Instance.towerButtonPressed = null;
    226.         }
    227.     }
    228.  
    229. }
    Thank you guys for the help
     
  2. chemicalcrux

    chemicalcrux

    Joined:
    Mar 16, 2017
    Posts:
    717
    What is this "Singleton" class? I do not recognize it.
     
  3. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,380
    Line 73 is this:

    GameManager.Instance.RoundEscaped += 1;

    So that tells us that GameManager.Instance is null. There are two possibilities- either GameManager.Instance is never assigned a value before this line executes, or else the object that GameManager.Instance refers to is deleted at some point.
     
  4. cdfertolo

    cdfertolo

    Joined:
    Feb 23, 2023
    Posts:
    2
    Thank you, I will have a look