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:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour {
//SerializeField - Allows Inspector to get access to private fields.
//If we want to get access to this from another class, we'll just need to make public getters
[SerializeField]
private Transform exitPoint;
[SerializeField]
private Transform[] wayPoints;
[SerializeField]
private float navigationUpdate;
[SerializeField]
private int healthPoints;
[SerializeField]
private int rewardAmount;
private int target = 0;
private Transform enemy;
private Collider2D enemyCollider;
private Animator anim;
private float navigationTime = 0;
private bool isDead = false;
public bool IsDead
{
get { return isDead; }
}
// Use this for initialization
void Start () {
enemy = GetComponent<Transform>();
enemyCollider = GetComponent<Collider2D>();
anim = GetComponent<Animator>();
Debug.Log("Starting register");
GameManager.Instance.RegisterEnemy(this);
Debug.Log("Registered");
}
// Update is called once per frame
void Update () {
if (wayPoints != null && !isDead)
{
//Lets use change how fast the update occurs
navigationTime += Time.deltaTime;
if(navigationTime > navigationUpdate)
{
//If enemy is not at the last wayPoint, keep moving towards the wayPoint
//otherwise move to the exitPoint
if(target < wayPoints.Length)
{
enemy.position = Vector2.MoveTowards(enemy.position, wayPoints[target].position, navigationTime);
}
else
{
enemy.position = Vector2.MoveTowards(enemy.position, exitPoint.position, navigationTime);
}
navigationTime = 0;
}
}
}
//If we trigger the collider2D.tag for checkpoints for finish.
//If it hits the checkpoints, increase the index and move to the next checkpoint
//otherwise enemy is at the finish line and should be destroyed.
void OnTriggerEnter2D(Collider2D collider2D)
{
if (collider2D.tag == "checkpoint")
target += 1;
else if (collider2D.tag == "Finish")
{
GameManager.Instance.RoundEscaped += 1;
GameManager.Instance.TotalEscape += 1;
GameManager.Instance.UnregisterEnemy(this);
GameManager.Instance.isWaveOver();
}
else if(collider2D.tag == "projectile")
{
Projectile newP = collider2D.gameObject.GetComponent<Projectile>();
enemyHit(newP.AttackStrength);
Destroy(collider2D.gameObject);
}
}
public void enemyHit(int hitPoints)
{
if(healthPoints - hitPoints > 0)
{
healthPoints -= hitPoints;
anim.Play("Hurt");
GameManager.Instance.AudioSource.PlayOneShot(SoundManager.Instance.Hit);
}
else
{
anim.SetTrigger("didDie");
die();
}
}
public void die()
{
isDead = true;
enemyCollider.enabled = false;
GameManager.Instance.TotalKilled += 1;
GameManager.Instance.AudioSource.PlayOneShot(SoundManager.Instance.Death);
GameManager.Instance.AddMoney(rewardAmount);
GameManager.Instance.isWaveOver();
}
}
All the things in this condition else if (collider2D.tag == “Finish”) (line 71) seems to doesn’t work properly.
That’s the GameManager script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public enum gameStatus
{
next, play, gameover, win
}
public class GameManager : Singleton<GameManager> {
//SerializeField - Allows Inspector to get access to private fields.
//If we want to get access to this from another class, we'll just need to make public getters
[SerializeField]
private int totalWaves = 10;
[SerializeField]
private Text totalMoneyLabel; //Refers to money label at upper left corner
[SerializeField]
private Text currentWaveLabel;
[SerializeField]
private Text totalEscapedLabel;
[SerializeField]
private GameObject spawnPoint;
[SerializeField]
private Enemy[] enemies;
[SerializeField]
private int totalEnemies = 3;
[SerializeField]
private int enemiesPerSpawn;
[SerializeField]
private Text playButtonLabel;
[SerializeField]
private Button playButton;
private int waveNumber = 0;
private int totalMoney = 10;
private int totalEscaped = 0;
private int roundEscaped = 0;
private int totalKilled = 0;
private int whichEnemiesToSpawn = 0;
private int enemiesToSpawn = 0;
private gameStatus currentState = gameStatus.play;
private AudioSource audioSource;
public List<Enemy> EnemyList = new List<Enemy>();
const float spawnDelay = 2f; //Spawn Delay in seconds
public int TotalMoney
{
get { return totalMoney; }
set
{
totalMoney = value;
totalMoneyLabel.text = totalMoney.ToString();
}
}
public int TotalEscape
{
get { return totalEscaped; }
set { totalEscaped = value; }
}
public int RoundEscaped
{
get { return roundEscaped; }
set { roundEscaped = value; }
}
public int TotalKilled
{
get { return totalKilled; }
set { totalKilled = value; }
}
public AudioSource AudioSource
{
get { return audioSource; }
}
// Use this for initialization
void Start () {
playButton.gameObject.SetActive(false);
audioSource = GetComponent<AudioSource>();
ShowMenu();
}
// Update is called once per frame
void Update () {
handleEscape();
}
//This will spawn enemies, wait for the given spawnDelay then call itself again to spawn another enemy
IEnumerator spawn()
{
if (enemiesPerSpawn > 0 && EnemyList.Count < totalEnemies)
{
for (int i = 0; i < enemiesPerSpawn; i++)
{
if (EnemyList.Count < totalEnemies)
{
Enemy newEnemy = Instantiate(enemies[Random.Range(0, enemiesToSpawn)]);
newEnemy.transform.position = spawnPoint.transform.position;
}
}
yield return new WaitForSeconds(spawnDelay);
StartCoroutine(spawn());
}
}
///Register - when enemy spawns
public void RegisterEnemy(Enemy enemy)
{
EnemyList.Add(enemy);
}
///Unregister - When they escape the screen
public void UnregisterEnemy(Enemy enemy)
{
EnemyList.Remove(enemy);
Destroy(enemy.gameObject);
}
///Destroy - At the end of the wave
public void DestroyAllEnemies()
{
foreach(Enemy enemy in EnemyList)
{
Destroy(enemy.gameObject);
}
EnemyList.Clear();
}
public void AddMoney(int amount)
{
TotalMoney += amount;
}
public void SubtractMoney(int amount)
{
TotalMoney -= amount;
}
public void isWaveOver()
{
totalEscapedLabel.text = "Escaped " + TotalEscape + "/10";
if (RoundEscaped + TotalKilled == totalEnemies)
{
if(waveNumber <= enemies.Length)
{
enemiesToSpawn = waveNumber;
}
setCurrentGameState();
ShowMenu();
}
}
public void setCurrentGameState()
{
if(totalEscaped >= 10)
{
currentState = gameStatus.gameover;
}
else if(waveNumber == 0 && (TotalKilled + RoundEscaped) == 0)
{
currentState = gameStatus.play;
}
else if(waveNumber >= totalWaves)
{
currentState = gameStatus.win;
}
else
{
currentState = gameStatus.next;
}
}
public void ShowMenu()
{
switch (currentState)
{
case gameStatus.gameover:
playButtonLabel.text = "Play Again!";
AudioSource.PlayOneShot(SoundManager.Instance.Gameover);
break;
case gameStatus.next:
playButtonLabel.text = "Next Wave";
break;
case gameStatus.play:
playButtonLabel.text = "Play";
break;
case gameStatus.win:
playButtonLabel.text = "Play";
break;
}
playButton.gameObject.SetActive(true);
}
public void playButtonPressed()
{
Debug.Log("Play Button Pressed");
switch (currentState)
{
case gameStatus.next:
waveNumber += 1;
totalEnemies += waveNumber;
break;
default:
totalEnemies = 3;
totalEscaped = 0;
TotalMoney = 10;
TowerManager.Instance.DestroyAllTower();
TowerManager.Instance.RenameTagsBuildSites();
totalMoneyLabel.text = TotalMoney.ToString();
totalEscapedLabel.text = "Escaped " + totalEscaped + "/10";
AudioSource.PlayOneShot(SoundManager.Instance.NewGame);
break;
}
DestroyAllEnemies();
TotalKilled = 0;
RoundEscaped = 0;
currentWaveLabel.text = "Wave " + (waveNumber + 1);
StartCoroutine(spawn());
playButton.gameObject.SetActive(false);
}
private void handleEscape()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
TowerManager.Instance.disableDragSprite();
TowerManager.Instance.towerButtonPressed = null;
}
}
}
Thank you guys for the help