I've been building a quiz game that randomly picks a gameobject from a list and after a question is completed it reloads the scene for a new question however, it states this 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.
GameManager.Start () (at Assets/Scripts/GameManager.cs:30)
And this is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour {
public static int betul1 = 0;
public static int betul2 = 0;
public static int salah1 = 0;
public static int salah2 = 0;
public GameObject[] questions;
private static List<GameObject> unansweredQuestions;
private GameObject currentQuestion;
[SerializeField]
private float transitionTime = 1f;
void Start()
{
if (unansweredQuestions == null || unansweredQuestions.Count == 0)
{
unansweredQuestions = questions.ToList<GameObject>();
}
GetQuestion();
currentQuestion.SetActive(true);
}
void GetQuestion()
{
int randomNumber = Random.Range(0,unansweredQuestions.Count);
currentQuestion = unansweredQuestions[randomNumber];
}
IEnumerator NextQuestion()
{
unansweredQuestions.Remove(currentQuestion);
//currentQuestion.SetActive(false);
yield return new WaitForSeconds(transitionTime);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
public void Yes()
{
if (betul1 == 1 && betul2 == 1)
{
Debug.Log("Congratulations! You're correct!");
StartCoroutine(NextQuestion());
}
if (salah1 == 1 && salah2 == 1)
{
Debug.Log("Sorry! You're wrong!");
StartCoroutine(NextQuestion());
}
if (betul1 == 1 && salah2 == 1)
{
Debug.Log("Your answer is invalid. Please fix it.");
}
if (betul2 == 1 && salah1 == 1)
{
Debug.Log("Your answer is invalid. Please fix it.");
}
}
}
I'm not sure what's wrong about it. I'm still relatively new to Unity so I would really appreciate it if you could point out what's causing this. Thank you in advance.
The error explains it all. On the first run of your game you'll find that GameManager.cs is attached to a valid GameObject and running fine. But when you reload a new scene all objects in a scene are destroyed and the second scene is loaded.
So there is no more GameManager context. The GameObject which your GameManager.cs script was associated with is destroyed. Since all data in GameManager.cs is static, I would suggest making it a static class or, if you want to keep the object around, use DontDestroyOnLoad
If you don't destroy any objects,
maybe some of the objects in the unansweredQuestions list are destroyed when you reload the scene.
So when you get the reference from GetQuestion(), it returns a reference to destroyed object. So when you try to set it active it throws this exception.
You can easily fix this by checking currentQuestion for null after getting it in GetQuestion().
But its better to fix the cause of the destroying of objects.
Try removing the currentQuestion from the unansweredQuestions, right after you get it in GetQuestion().
If there are other scripts accessing questions list, the problem may be there.
If the objects that are in questions list are destroyed, the objects in unansweredQuestions will be destroyed too.
*Edit. ok so nvm the GameManager. the error is on line 30, denoted by your output.
line 30 is:
currentQuestion.SetActive(true);
this error is saying that currentQuestion is empty. if your reloading the scene you will need to set this to a question, in void Start() before trying to set it to active.
Related
I am trying to reference my speechBubbletTOSpawn object that was in my SpeechAppearsSETACTIVE script, but the only way I know how to do this is to make the speechBubbletTOSpawn object in the SpeechAppearsSETACTIVE code a "public static" variable.
Is there another way to do this?
When I do it this way I get the following error:
Assets/RunOnce.cs(8,37): error CS0120: An object reference is required for the non-static field method, or property 'SpeechAppearsSETACTIVE.speechBubbleTOSpawn'
I am trying to make a speech bubble appear when Torso 2 is selected, and this happens in the SpeechAppearsSETACTIVE script. The current script below is supposed to check if this has already been done ONCE, and if it has, to not make the speech bubble appear on the second instance of Torso 2 being selected.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RunOnce : MonoBehaviour
{
int FirstTime; //boolean true/false
private GameObject YesSpeech => SpeechApearsSETACTIVE.speechBubbleTOSpawn;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
FirstTime = PlayerPrefs.GetInt("FirstTime"); //obtains int value
// 0 = true
// 1 = false
if (FirstTime == 0)
{
YesSpeech.SetActive(true);
}
else
{
YesSpeech.SetActive(false);
}
}
}
The error says it all, you need to create a reference for your script SpeechApearsSETACTIVE in order to have access to it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RunOnce : MonoBehaviour
{
int FirstTime; //boolean true/false
public SpeechApearsSETACTIVE speechApearsSETACTIVE;
private GameObject YesSpeech;
// Start is called before the first frame update
void Start()
{
YesSpeech = speechApearsSETACTIVE.speechBubbleTOSpawn;
}
// Update is called once per frame
void Update()
{
FirstTime = PlayerPrefs.GetInt("FirstTime"); //obtains int value
// 0 = true
// 1 = false
if (FirstTime == 0)
{
YesSpeech.SetActive(true);
}
else
{
YesSpeech.SetActive(false);
}
}
}
Now you can simply just drag and drop the gameObject that the SpeechApearsSETACTIVE script is attached to it through Unity's Editor.
If your SpeechApearsSETACTIVE is not attached anywhere, then you should create an empty gameObject and just add the script as a component and use that one.
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 9 months ago.
This is my code that is receiving the error on the playerScore increment line. The code is in a prefab that is generated many times, and if it hits the player, it is destroyed and the player's score is incremented. Currently the code will destroy on collision with anything that does not have the name "Player" due to the error once the if statement is executed.
private void OnCollisionEnter2D(Collision2D collisionInfo)
{
Debug.Log($#"Collision name = {collisionInfo.collider.name}");
if (collisionInfo.collider.name == "Player")
{
GetComponent<GameMechanics>().playerScore++;
}
GameObject e = Instantiate(explosion) as GameObject;
e.transform.position = transform.position;
Destroy(gameObject);
}
This is my code for the referenced variable, I just cannot tell what I am doing wrong here, I want the object destroyed in all cases, but want the score incremented when the player hits it.Is the object being destroyed preventing the changes to be made to the player score?
public class GameMechanics : MonoBehaviour
{
public int playerScore;
// Start is called before the first frame update
void Start()
{
playerScore = 0;
}
// Update is called once per frame
void Update()
{
Debug.Log($#"Player Score = {playerScore} ");
}
}
Thanks in advance!
This is because you are using GetComponent in a class that does not have it and should find GameMechanics in it, but the result is null.
GetComponent<GameMechanics>().playerScore++; // GameMechanics dosen't found..
One way is to give GameMechanic to the other object's is through the inspector:
public GameMechanics Mechanics;
private void OnCollisionEnter2D(Collision2D collisionInfo)
{
if (collisionInfo.collider.name == "Player") Mechanics.playerScore++;
}
However There are other ways to reference GameMechanic and Manager classes in other objects, the best of which is Singleton. here is a simple way to solve your problem easy and that is to use FindObjectOfType. Change the code below and it will probably be fixed.
if (collisionInfo.collider.name == "Player")
{
FindObjectOfType<GameMechanics>().playerScore++; // replace it with this way
}
NullReferenceExceptions occur when the code attempts to access a variable which has not been initialized properly. In your case, this could be because of several reasons.
Ensure the gameobject is set to active in the scene
Ensure the script object is enabled on the gameobject
If these do not help, you can try making your variable static, and setting it to an ititial value of 0.
Static variables are loaded before everything else, and accessible by most other classes, so this may be able to fix your issue.
so I'm wanting to pause the game once the amount of enemies hits 0. So I'm using GameObject.FindGameObjectsWithTag("Enemy").Length to find the number of enemies. I put this in a function that's called right when the enemies are instantiated so I can see the length go to 4, as there's 4 enemies spawning. When an enemy is killed the function is called again where the length is printed to console again. For some reason, on the first enemy killed the count repeats with a 4 again despite there only being 3 enemies. Once another enemy is killed it reports 3 when there's actually 2 and so on until I get to 1 when there's 0 enemies.
Here's the first snippet of code:
public class EnemyList : MonoBehaviour
{
public List<GameObject> weakMobs = new List<GameObject>();
public List<GameObject> mediumMobs = new List<GameObject>();
public List<GameObject> bossMobs = new List<GameObject>();
public List<Transform> spawningChildren = new List<Transform>();
public static int mobCount;
void Start()
{
for (int i = 0; i < spawningChildren.Count; i++)
{
GameObject newWeakMob = Instantiate(weakMobs[0], spawningChildren[Random.Range(0, 4)]) as GameObject;
}
CheckMobCount();
}
public void CheckMobCount()
{
mobCount = GameObject.FindGameObjectsWithTag("Enemy").Length;
print(mobCount);
}
The next piece of code is where the enemy is killed and the CheckMobCount() is called again.
public void TakeDamage()
{
enemyCurrentHealth -= 25;
enemyHealthBar.SetHealth(enemyCurrentHealth);
if (enemyCurrentHealth == 0)
{
Destroy(this.gameObject);
enemyList.CheckMobCount();
//needs death animations
}
}
Here's the console messages:
Console of printed lengths
I'm self taught so I apologize if this is elementary. I've tried doing this several different ways and this is the closest I've been but I'm open to new ideas as well.
Thank you!!
As noted in this answer, the object is not actually destroyed in the current frame.
From the documentation:
The object obj is destroyed immediately after the current Update loop… Actual object destruction is always delayed until after the current Update loop, but is always done before rendering.
I also agree that using DestroyImmediate() is a bad idea.
Ultimately, your question seems to really be about pausing the game when the enemy count reaches 0, which unfortunately hasn't actually been answered yet.
In fact, you don't really need to do anything different except move the check for the enemy count to the beginning of the Update() method, and pause the game there if it's 0. Then you'll find that the component for the enemy has been destroyed at that point.
Presumably enemies are spawned before the update loop starts (i.e. before the first frame), but if not then you can use whatever logic you're already using to decide that new enemies need to be spawned, to detect the fact that you haven't spawned any yet and avoid pausing before the enemies have spawned.
Here you have attached your script to your enemy instances. And they are still alive when you are querying for the number of enemies left.
You should do the following:
public class Enemy: MonoBehaviour
{
public static int EnemyCount = 0;
private void Start()
{
EnemyCount++;
}
private void OnDestroy()
{
EnemyCount--;
}
}
And then you can query the enemy count from anywhere but just excessing the EnemyCount by Enemy.EnemyCount.
If you want to get a more difficult example then you can check out this Game Dev tutorial: https://www.youtube.com/watch?v=LPBRLg4c5F8&t=134s
Destroy is actually executed at the end of the frame. There is DestroyImmediate that is executed immidiatelly but it's not recommended to be used. What I would do is to add a field or a property to identify whether the enemy is still alive and then to check against it. Something like:
class Enemy : MonoBehaviour
{
public bool IsAlive { get; set; } = true;
}
public class EnemyList : MonoBehaviour
{
//...
public void CheckMobCount()
{
mobCount = GameObject.FindGameObjectsWithTag("Enemy").Select(x => x.GetComponent<Enemy>()).Count(x => x.IsAlive);
print(mobCount);
}
}
And then:
public void TakeDamage()
{
enemyCurrentHealth -= 25;
enemyHealthBar.SetHealth(enemyCurrentHealth);
if (enemyCurrentHealth == 0)
{
Destroy(this.gameObject);
this.GetComponent<Enemy>().IsAlive = false;
enemyList.CheckMobCount();
//needs death animations
}
}
This can be further optimized to store the Enemy somewhere and not use GetComponent every time but you get the idea.
As already mentioned by others the issue is that Destroy is executed delayed.
Actual object destruction is always delayed until after the current Update loop, but is always done before rendering.
You could simply count only the GameObjects that are still alive, those for which the bool operator is true.
Does the object exist?
It will be false for objects destroyed in that same frame.
E.g. using Linq Count
using System.Linq;
....
mobCount = GameObject.FindGameObjectsWithTag("Enemy").Count(e => e);
which basically equals doing
mobCount = 0;
foreach(e in GameObject.FindGameObjectsWithTag("Enemy"))
{
if(e) mobCount++;
}
There is no need for an additional property or Component.
I am suggesting you to use “DestroyImmediate” instead of “Destroy”,Then look at the result.
I have a better idea, why not just use static variables when spawning enemies?
void Start()
{
for (int i = 0; i < spawningChildren.Count; i++)
{
GameObject newWeakMob = Instantiate(weakMobs[0],
spawningChildren[Random.Range(0, 4)]) as GameObject;
mobCount++;
}
}
Do not use Linq
Do not use DestroyImmediate (it will freeze and bug your game, probably)
Avoid FindGameObjectsWithTag in loops, only in initialization.
Track your enemies in an array or list
When you destroy an enemy, remove it's reference from the list
Use the list count/length to get the real actual number.
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 4 years ago.
So I'm getting an error which says
Object reference not set to an instance of an object Explode.LateUpdate () (at Assets/Scripts/Explode.cs:40)
I'm not sure why I get this error as everything works fine and I have the exact same script in a different scene without the error message. I'd love any help please.
I'm exploding my player with different coloured boxes.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Explode : MonoBehaviour {
[System.Serializable]
public class ExplodeColours
{
public Color32[] Colours;
}
public static bool explode, explodeOnce;
public ParticleSystem Explodes;
private ParticleSystem explosionSystem;
public Transform player;
public List<ExplodeColours> ColoursList;
// Use this for initialization
void Start () {
PlayerPrefs.SetInt("SelectedChar", 0);
PlayerPrefs.Save();
explodeOnce = false;
explode = false;
}
// Update is called once per frame
void Update () {
if (explode == true)
{
explodeOnce = true;
PlayerExplode();
}
}
void LateUpdate()
{
if (explode)
{
ParticleSystem.Particle[] Particles = new ParticleSystem.Particle[explosionSystem.main.maxParticles];
//ParticleSystem.Particle[] Particles = new ParticleSystem.Particle[Explodes.main.maxParticles];
int NumParticlesAlive = explosionSystem.GetParticles(Particles);
for (int i = 0; i < NumParticlesAlive; i++)
{
Particles[i].startColor = (ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours[Random.Range(0, ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours.Length)]);
}
explosionSystem.SetParticles(Particles, NumParticlesAlive);
explode = false;
}
}
void PlayerExplode()
{
explosionSystem = Instantiate(Explodes, player.position, player.rotation);
explosionSystem.Play();
}
}
If this is line 40 (I had to count):
ParticleSystem.Particle[] Particles = new ParticleSystem.Particle[explosionSystem.main.maxParticles];
Then explosionSystem or main or maxParticles is null. I'm going to close this as a duplicate, this error is so common and the way to resolve it is easy.
But a break point on the line of code, when the code control halts, check which object is null and make sure its set.
EDIT:
Use the GetComponent to initialize the explosionSystem variable in the Start function.First, find the GameObject the ParticleSystem is attached to the use GetComponent to get the ParticleSystem.
private ParticleSystem explosionSystem;
void Start()
{
GameObject obj = GameObject.Find("NameOfObjectParticleSystemIsAttachedTo");
explosionSystem = obj.GetComponent<ParticleSystem>();
}
Also, inside the for loop, you tried to use Particles's startColor property without initializing each one. You can do that with Particles[i] = new ParticleSystem.Particle();.
Change:
for (int i = 0; i < NumParticlesAlive; i++)
{
Particles[i].startColor
= (ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours[Random.Range(0, ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours.Length)]);
}
to
for (int i = 0; i < NumParticlesAlive; i++)
{
Particles[i] = new ParticleSystem.Particle();
Particles[i].startColor
= (ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours[Random.Range(0, ColoursList[PlayerPrefs.GetInt("SelectedChar")].Colours.Length)]);
}
To avoid the null reference error, make sure all variables (which are using) has values other wise at least assign default values in constructor level
I'm trying to apply a null on an array. The arrays are points where game prefabs are being instantiated. At some point in the game, these spawn points may be destroyed. When this happens, the code is still trying to access that spawn point even though it no longer exists, so I'm trying to make it null. So far I have been unsuccessful. This is the last thing in the game I need to fix. Help is appreciated.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
public class podControl : MonoBehaviour {
public Transform [] spawns;
public float spawnTime = 6f;
public float secondSpawnTime = 3f;
public GameObject podPrefab;
void Start ()
{
InvokeRepeating ("landingPod", spawnTime, secondSpawnTime);
}
void landingPod ()
{
int spawnIndex = Random.Range (0, spawns.Length);
if (spawns != null) {
Instantiate (podPrefab, spawns [spawnIndex].position, spawns [spawnIndex].rotation);
}
}
}
shouldn't it be
if(spawns[spawnIndex] != null)
Otherwise it's checking to see if the array is not null rather than the entry