So I have this project that I am basically done with, by that, I mean that I have everything that is required, but something is screwing up. I was tasked to make a GUI where one element changes the game's speed, and it works when running the game for the first time, however as soon as I destroy an enemy object (it spawns another one) and then I try adjusting the speed, the missing reference exception occurs, and I don't know where it's coming from.
#MissingReferenceException, I need help
1 messages · Page 1 of 1 (latest)
What steps did you take to debug the issue?
Forgive me, I'm still fairly new to unity, but I've tried looking at the console to see what was up, and I see some scripts mentioned within the error. I have an idea as to why it's happening, but I don't necessarily know code wise.
If you're posting in #archived-code-general , you're expected to be able to do some debugging.
Either way, can't help you without more details(error message, cod, etc..)
I see, I'll get to the details. When the game is built, everything is already in the level except the enemy prefab, so there's a level controller that spawns in the enemy. When the enemy gets hit by a ray-cast from the player clicking, it destroys the game object, which I would assume is where since in one of the other scripts or methods refers to that initial game object, that's why it's freaking out.
I'll send in the scripts I believe are at fault now
This one is the level controller
using System.Collections.Generic;
using UnityEngine;
public class LevelController : MonoBehaviour
{
[SerializeField] private GameObject enemyPrefab;
private GameObject enemy;
private List<GameObject> enemies = new List<GameObject>();
private int score = 0;
private Playercontroller playerMovementController;
void Start()
{
playerMovementController = FindObjectOfType<Playercontroller>();
}
void Update()
{
if (enemy == null)
{
enemy = Instantiate(enemyPrefab) as GameObject;
enemy.transform.position = new Vector3(11f, 1.5f, -10f);
enemies.Add(enemy);
float angle = Random.Range(0, 360);
enemy.transform.Rotate(0, angle, 0);
}
}
public void SetScore (int newScore)
{
score = newScore;
}
public int GetScore()
{
return score;
}
public void UpdateMovementSpeedOfAllAis(float newSpeed)
{
foreach(GameObject enemyAI in enemies)
{
WanderingAI AIMovementController = enemyAI.GetComponent<WanderingAI>();
AIMovementController.SetSpeed(newSpeed);
}
}
public void UpdatePlayerSpeed(float newSpeed)
{
playerMovementController.SetSpeed(newSpeed);
}
}
This script is used for when the enemy object gets hit with a ray-cast
using System.Collections.Generic;
using UnityEngine;
public class ReactiveTargets : MonoBehaviour{
[SerializeField] LevelController gameController;
[SerializeField] UIController ui;
void Start()
{
gameController = FindObjectOfType<LevelController>();
ui = FindObjectOfType<UIController>();
}
public void ReactToHit()
{
WanderingAI Behavior = GetComponent<WanderingAI>();
if (Behavior != null)
{
Behavior.SetAliveStatus(false);
}
int newScore = gameController.GetScore() + 1;
gameController.SetScore(newScore);
ui.UpdateScoreText(newScore.ToString());
StartCoroutine(Die());
}
IEnumerator Die()
{
transform.Rotate(-75, 0, 0);
yield return new WaitForSeconds(1.5f);
Destroy(this.gameObject);
}
}
The most important thing is missing - the error details.
The error was "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."
You're omitting the most important information
The error message tells you what line of code threw the error.
Without it, it's basically useless
The thing is that it shows a whole bunch of details
It also contains a stack trace of what led to that call.
Yes, and it's all potentially important for resolving the issue.
LevelController.UpdateMovementSpeedOfAllAis (System.Single newSpeed) (at Assets/Scripts/LevelController.cs:48)
SettingsPopup.OnChangePlayerSpeed (System.Single speed) (at Assets/Scripts/SettingsPopup.cs:31)
UnityEngine.Events.InvokableCall`1[T1].Invoke (T1 args0) (at <acc21e65c0ae4adf80ac80bfa5d3f603>:0)
UnityEngine.Events.UnityEvent`1[T0].Invoke (T0 arg0) (at <acc21e65c0ae4adf80ac80bfa5d3f603>:0)
UnityEngine.UI.Slider.Set (System.Single input, System.Boolean sendCallback) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Slider.cs:519)
UnityEngine.UI.Slider.OnMove (UnityEngine.EventSystems.AxisEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Slider.cs:657)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IMoveHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:120)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:262)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:385)```
This was from the console
This is the stack trace that led to the error. You can see the latest calls in the stack from top to bottom.
Notice how the first thing is get component. That's the call that threw the error.
So the first line of the stack trace is what causes this sort of domino effect?
The next thing is a line of code from your script - LevelController.UpdateMovementSpeedOfAllAis on line 48
No domino effect involved. It simply tells you the history of calls that let to the exception.
Something on that line must be null.
The line would be WanderingAI AIMovementController = enemyAI.GetComponent<WanderingAI>(); so it has to be within another script
You can use the debugger or debug logs to further investigate what is being null and why.
enemyAI must be null then.
Also I'm starting to understand the stack trace a little more, now that I'm starting to see that the cs:XX refers to a specific line
Seeing how it's called from an event(further in the stack trace), I can assume that you don't unsubscribe from an event when an object is destroyed.
Actually, nevermind that. Just had a look at the code and it's probably not that.
I'd guess that you just don't remove the destroyed enemy from the enemies list.
Here's that wanderingAI script that the other thing is calling:
using System.Collections.Generic;
using UnityEngine;
public class WanderingAI : MonoBehaviour
{
public float obstacleDetectionRange = 5.0f;
public float movementSpeed = 3.0f;
[SerializeField] GameObject FireballPrefab;
GameObject spawnedFireball;
bool isAlive;
void Start()
{
isAlive = true;
}
void Update()
{
if (isAlive)
{
transform.Translate(0, 0, movementSpeed * Time.deltaTime);
Ray ray = new Ray(transform.position, transform.forward);
RaycastHit hit;
bool objectWasHit = Physics.SphereCast(ray, 0.75f, out hit);
if (objectWasHit)
{
GameObject hitObject = hit.transform.gameObject;
if (hitObject.GetComponent<PlayerCharacter>())
{
if (spawnedFireball == null)
{
spawnedFireball = Instantiate(FireballPrefab);
spawnedFireball.transform.position =
transform.TransformPoint(Vector3.forward * 1.5f);
spawnedFireball.transform.rotation = transform.rotation;
}
}
else if (hit.distance < obstacleDetectionRange)
{
float angle = Random.Range(-110, 110);
transform.Rotate(0, angle, 0);
}
}
}
}
public void SetAliveStatus (bool status)
{
isAlive = status;
}
public void SetSpeed(float newSpeed)
{
movementSpeed = newSpeed;
}
}
Looking at this, I'm not sure if it is this script that is the problem
Because this only really controls the enemy's movement and shooting a projectile if the ray-cast from them hits the player object
The one thing I can see as being a contributor to the problem is the movementSpeed line, since that one was a new addition for this project
Which to give context, most of my class was just watching videos of the professor just making the game and just following along
I'll probably have to change if the enemy gets hit, it doesn't destroy it, but rather teleports it back to it's starting position
Or just make sure to remove the destroyed enemies from the list...🤷♂️
I'm not sure how to do that, sorry if that seems a bit annoying or more of a "This should be obvious" type of thing
I just realized where the list is, now to figure out how to modify said list into removing the destroyed enemy
There are many ways. Either check in your level controller wether any enemies in the list have been destroyed(became null), and if so remove them from the list, or manage the destruction of the enemy in the LevelController so that you can also remove it from the list, or raise an event in the enemy class when it's destroyed. The LevelController then can subscribe to that event to remove the destroyed enemy from the list.
There's that conditional statement of if(enemy == null), so I'm not sure the first option would be the best, but that's what I'm thinking.
Unless I make another conditional statement to check if the destroy method was used
I'm not sure what that piece of code is for and how it's relevant to the issue at all.
It doesn't check the enemies in the list.
This part in the level controller
{
if (enemy == null)
{
enemy = Instantiate(enemyPrefab) as GameObject;
enemy.transform.position = new Vector3(11f, 1.5f, -10f);
enemies.Add(enemy);
float angle = Random.Range(0, 360);
enemy.transform.Rotate(0, angle, 0);
}```
If there are no enemies in the level already, spawn a new one at those coordinates
Yes, I've seen that.
Ok, but what does it have to do with removing destroyed enemies?
Here's a question for you: let's say you spawn 2 enemies. Then one enemy is being destroyed. What would your enemies list contain?
I want to say that there would be one enemy, because one of the other enemy objects got destroyed
But that doesn't remove them from the list, no?
Nope, it doesn't remove them from the list.
That would be magic and very undesirable behavior
Okay, so destroying the enemy just gets rid of the object in the game space, but not the list
It destroys the underlying object. References are not part of the object.
If you place a tag/label (reference) on a box that contains an apple saying that there's an apple inside, then remove the apple from the box, the lable won't go anywhere. It would just point to an empty box.
So I put the enemy list to public, and yeah, it doesn't remove the enemy
So I'm attempting to modify the code, but I have no idea how I can implement the list.RemoveAt(index) method. I've tried putting it in the Die() corroutine, but I end up with a null reference exception, I tried putting it in it's own method, but didn't work. All I know is I want to get rid of the specific enemy when it gets hit
I mentioned several ways you can do it earlier.
I did it, with a bit of a hiccup, but I don't care
{
transform.Rotate(-75, 0, 0);
yield return new WaitForSeconds(1.5f);
gameController.enemies.RemoveAt(0);
Destroy(this.gameObject);
}```
All I did was add this before destroying the game object, and no errors
The only nitpick I will say is that the new enemies don't keep the same speed as the previous one when killed