Hi there,
I’m trying to continue the Laser Defender project from 2D course and I’m getting in some point that I can’t solve by myself. I’m getting an error related to a button GameObject that seems to be destroyed in some moment at start one level (but I don’t know why or where), and when a StartCoroutine try to trigger the level change it returns a null error.
I’m sharing code involved (may it’s more than what I’m pasting here and the Unity console error, in order to locate problem.
I’ve tried on some ways to modify this unexpected behavior, but I can’t go ahead. Game stays stuck in change level creating and destroying infinitely the new scene, or simply waiting for a button that never arrives.
I’ve checked that all components are linked in inspector, all prefabs, scripts and other stuff inside unity seems to be ok but nothing runs as expected.
Thanks for your help!
PS: Methods and variables names are written in my native language, Catalan, in order to help me understand and follow better what’s happening inside the game, but it’s easy to know what every function does when inspect inside.
Unity error
MissingReferenceException: The object of type ‘GameObject’ has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
Botons.MostrarBotoIniciar () (at Assets/Scripts/Botons.cs:28)
Botons.MostrarBotons () (at Assets/Scripts/Botons.cs:24)
CarregadorEscenes.CarregarProperaEscena () (at Assets/Scripts/CarregadorEscenes.cs:46)
SessioJoc+d__13.MoveNext () (at Assets/Scripts/SessioJoc.cs:56)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator enumerator, System.IntPtr returnValueAddress) (at /home/builduser/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)
CarregadorEscenes.cs (SceneLoader)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class CarregadorEscenes : MonoBehaviour
{
[SerializeField] float segonsAbansCanviEscena = 3f;
[SerializeField] int properaEscena;
SessioJoc sessioEntrant;
Botons botoIniciar;
/* private void Awake() {
Singleton();
}
private void Singleton() {
int comptadorSessions = FindObjectsOfType(GetType()).Length;
if (comptadorSessions > 1) {
gameObject.SetActive(false);
Destroy(gameObject);
} else {
DontDestroyOnLoad(gameObject);
}
} */
public void Start() {
sessioEntrant = FindObjectOfType<SessioJoc>();
botoIniciar = FindObjectOfType<Botons>();
}
public void CarregarEscenaInicial() {
sessioEntrant.ResetPuntuacio();
botoIniciar.MostrarBotons();
SceneManager.LoadScene(0);
}
public void CarregarPantalla1() {
SceneManager.LoadScene(1);
}
public void CarregarProperaEscena() {
sessioEntrant.ResetEnemics();
botoIniciar.MostrarBotons();
SceneManager.LoadScene(0);
}
public void FinalDePartida() {
StartCoroutine(CorutinaFinalDePartida());
}
private IEnumerator CorutinaFinalDePartida() {
yield return new WaitForSeconds(segonsAbansCanviEscena);
CarregarEscenaInicial();
}
public void Sortir() {
Application.Quit();
}
}
Botons.cs (Buttons)
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Botons : MonoBehaviour
{
Text text;
GameObject botoIniciar;
// GameObject botoSortir;
private void Start() {
botoIniciar = GameObject.Find("BotoIniciar");
// botoSortir = GameObject.Find("BotoSortir");
text = botoIniciar.GetComponentInChildren<Text>();
}
public void AmagarBotons() {
AmagarBotoIniciar();
AmagarBotoSortir();
}
public void MostrarBotons() {
MostrarBotoIniciar();
}
private void MostrarBotoIniciar() {
botoIniciar.SetActive(true);
}
public void Sortir() {
Application.Quit();
}
public void AmagarBotoSortir() {
// botoSortir.SetActive(false);
}
public void AmagarBotoIniciar() {
StartCoroutine(CorutinaAmagarBotoIniciar());
}
private IEnumerator CorutinaAmagarBotoIniciar() {
// botoIniciar.GetComponent<Button>().interactable = false;
yield return new WaitForSeconds(1f);
for (int i = 3; i > 0; i--) {
text.text = (i + "!").ToString();
yield return new WaitForSeconds(0.5f);
}
text.text = "Go!";
yield return new WaitForSeconds(1.5f);
botoIniciar.SetActive(false);
}
}
And finally SessioJoc.cs (GameSession)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class SessioJoc : MonoBehaviour
{
[SerializeField] int puntuacioJugador = 0;
[SerializeField] int puntuacioActual = 0;
[SerializeField] Text uiPuntuacio;
[SerializeField] bool enemicsIniciats = false;
[SerializeField] int nombreEnemics;
[SerializeField] float segonsAbansCanviEscena = 3f;
[SerializeField] GameObject nouBotoPrefab;
// components capturats
Enemic enemics;
CarregadorEscenes finalEscena;
private void Awake() {
Singleton();
}
private void Singleton() {
int comptadorSessions = FindObjectsOfType<SessioJoc>().Length;
if (comptadorSessions > 1) {
gameObject.SetActive(false);
Destroy(gameObject);
} else {
DontDestroyOnLoad(gameObject);
}
}
// Start is called before the first frame update
void Start()
{
finalEscena = FindObjectOfType<CarregadorEscenes>();
if (!finalEscena) { return; }
uiPuntuacio.text = puntuacioActual.ToString();
}
void Update() {
if (enemicsIniciats) {
nombreEnemics = FindObjectsOfType<Enemic>().Length;
if (nombreEnemics <= 0) {
StartCoroutine(SollicitarProperaEscena());
}
}
}
private IEnumerator SollicitarProperaEscena() {
yield return new WaitForSeconds(segonsAbansCanviEscena);
finalEscena.CarregarProperaEscena();
}
public void IniciarEnemics() {
enemicsIniciats = true;
}
public void Puntuacio(int sumaPunts) {
puntuacioActual += sumaPunts;
uiPuntuacio.text = puntuacioActual.ToString();
}
public void ResetEnemics() {
enemicsIniciats = false;
}
public void ResetPuntuacio() {
puntuacioActual = 0;
Destroy(gameObject);
}
public int ObtenirPuntuacio() {
return puntuacioJugador;
}
}
Other piece of code that manipulates buttons is inside the enemy spawner:
GeneradorEnemics.cs (EnemySpawner, only pieces that I think are directly involved on this)
public class GeneradorEnemics : MonoBehaviour
{
[SerializeField] List<ConfigOnada> configsOnades;
[SerializeField] int onadaInici = 0;
[SerializeField] bool infinit = false;
Botons botoIniciar;
SessioJoc iniciarEnemics;
// Start is called before the first frame update
/* IEnumerator Start()
{
do {
yield return StartCoroutine(GenerarTotesLesOnades());
} while (infinit);
} */
private void Start() {
botoIniciar = FindObjectOfType<Botons>();
iniciarEnemics = FindObjectOfType<SessioJoc>();
IniciarPantalla();
}
public void IniciarPantalla() {
botoIniciar.AmagarBotons();
StartCoroutine(GenerarTotesLesOnades());
}