I have a SO in my quiz game. When i reference it in the inspector unity freezes. But when i dont, it works without loading the questions and throws nullreferenceexception error
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; using TMPro; using UnityEngine.SceneManagement;
public class QueManager : MonoBehaviour { #region Variables
private Data data = new Data();
[SerializeField] GameEvents events = null;
[SerializeField] Animator timerAnimtor = null;
[SerializeField] TextMeshProUGUI timerText = null;
[SerializeField] Color timerHalfWayOutColor = Color.yellow;
[SerializeField] Color timerAlmostOutColor = Color.red;
private Color timerDefaultColor = Color.white;
private List<AnswerData> PickedAnswers = new List<AnswerData>();
private List<int> FinishedQuestions = new List<int>();
private int currentQuestion = 0;
private int timerStateParaHash = 0;
private IEnumerator IE_WaitTillNextRound = null;
private IEnumerator IE_StartTimer = null;
private bool IsFinished
{
get
{
return (FinishedQuestions.Count < data.Questions.Length) ? false : true;
}
}
#endregion
#region Default Unity Methods
void OnEnable()
{
events.UpdateQuestionAnswer += UpdateAnswers;
}
void OnDisable()
{
**events.UpdateQuestionAnswer -= UpdateAnswers;** This is the troublesome part
}
void Awake()
{
**events.CurrentFinalScore = 0;** This is the troublesome part
}
void Start ()
{
events.StartupHighscore = PlayerPrefs.GetInt(GameUtility.SavePrefKey);
timerDefaultColor = timerText.color;
LoadData();
timerStateParaHash = Animator.StringToHash("TimerState");
var seed = UnityEngine.Random.Range(int.MinValue, int.MaxValue);
UnityEngine.Random.InitState(seed);
Display();
}
#endregion
Below is the scriptable object
using System.Collections; using System.Collections.Generic; using UnityEngine;
[CreateAssetMenu(fileName = "GameEvents", menuName = "Quiz/new GameEvents")] public class GameEvents : ScriptableObject { public delegate void UpdateQuestionUICallback(Question question); public UpdateQuestionUICallback UpdateQuestionUI;
public delegate void UpdateQuestionAnswerCallback(AnswerData pickedAnswer); public UpdateQuestionAnswerCallback UpdateQuestionAnswer;
public delegate void DisplayResolutionScreenCallback(UIManager.ResolutionScreenType type, int score); public DisplayResolutionScreenCallback DisplayResolutionScreen;
public delegate void ScoreUpdateCallback(); public ScoreUpdateCallback ScoreUpdated;
[HideInInspector]
public int CurrentFinalScore;
[HideInInspector]
public int StartupHighscore;
}
Hi, can you fix the formating for the ScriptableObject? I'm not sure I'm reading it properly.
That said, It doesn't seem like the problem is in that code. Unity freezes are almost always the result of an infinite loop. It's not hard to cause infinite loops when using multiple delegates that can be accessed from all kinds of different places. I bet that's the cause of your problems: a delegate call results in a chain of methods that calls that delegate again. When you remove the ScriptableObject, the delegates can't be assigned, so the infinite loop is avoided.
To debug it, it can be useful to access the log file: Right click on the Console tab ( the part that says "Console"), click "Open Editor Log", open it with NotePad, and click "Save As..." to find where the log is saved in your computer. Now you can read the log even if the editor froze. The next steps are to put some Debug.Logs in the code that executes those Delegates and in the methods that get added to those Delegates, cause the freeze to happen, force-close Unity, and Open the Log file to get an idea of what Delegates are part of the problem.
Additionally, if you share the delegate calling code and the listener methods, we might be able to help you find what is causing the infinite loop.
@oscarAbraham i tried what you said about the log files. It displayed the same concerns as the console without any helpful points...
It's expected to be the same. The point it's that you can't normally see the logs while Unity is frozen; now you can put some Debug.Log("text");
in your delegates to see in the logs what it's the order they are called in and which ones are involved in the freezing. Alternatively, if you know how to use your IDE's debugger, you could put some stops to manually step through your callbacks and find that information.
Answer by flaxibitz · May 07, 2021 at 10:04 PM
Thank you very much @oscarAbraham. below is the code where the delegates were created:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "GameEvents", menuName = "Quiz/new GameEvents")]
public class GameEvents : ScriptableObject {
public delegate void UpdateQuestionUICallback(Question question);
public UpdateQuestionUICallback UpdateQuestionUI;
public delegate void UpdateQuestionAnswerCallback(AnswerData pickedAnswer);
public UpdateQuestionAnswerCallback UpdateQuestionAnswer;
public delegate void DisplayResolutionScreenCallback(UIManager.ResolutionScreenType type, int score);
public DisplayResolutionScreenCallback DisplayResolutionScreen;
public delegate void ScoreUpdateCallback();
public ScoreUpdateCallback ScoreUpdated;
[HideInInspector]
public int CurrentFinalScore;
[HideInInspector]
public int StartupHighscore;
Here is where the problematic code is:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using TMPro;
using UnityEngine.SceneManagement;
public class QueManager : MonoBehaviour
{
#region Variables
private Data data = new Data();
[SerializeField] GameEvents events = null;
[SerializeField] Animator timerAnimtor = null;
[SerializeField] TextMeshProUGUI timerText = null;
[SerializeField] Color timerHalfWayOutColor = Color.yellow;
[SerializeField] Color timerAlmostOutColor = Color.red;
private Color timerDefaultColor = Color.white;
private List<AnswerData> PickedAnswers = new List<AnswerData>();
private List<int> FinishedQuestions = new List<int>();
private int currentQuestion = 0;
private int timerStateParaHash = 0;
private IEnumerator IE_WaitTillNextRound = null;
private IEnumerator IE_StartTimer = null;
private bool IsFinished
{
get
{
return (FinishedQuestions.Count < data.Questions.Length) ? false : true;
}
}
#endregion
#region Default Unity Methods
void OnEnable()
{
events.UpdateQuestionAnswer += UpdateAnswers;
}
void OnDisable()
{
events.UpdateQuestionAnswer -= UpdateAnswers; //This Line has Issues...
}
void Awake()
{
events.CurrentFinalScore = 0; //This Line has Issues...
}
void Start ()
{
events.StartupHighscore = PlayerPrefs.GetInt(GameUtility.SavePrefKey);
timerDefaultColor = timerText.color;
LoadData();
timerStateParaHash = Animator.StringToHash("TimerState");
var seed = UnityEngine.Random.Range(int.MinValue, int.MaxValue);
UnityEngine.Random.InitState(seed);
Display();
}
#endregion
public void UpdateAnswers(AnswerData newAnswer)
{
if(data.Questions[currentQuestion].Type == AnswerType.Single)
{
foreach (var answer in PickedAnswers)
{
if(answer != newAnswer)
{
answer.Reset();
}
PickedAnswers.Clear();
PickedAnswers.Add(newAnswer);
}
}
else
{
bool alreadyPicked = PickedAnswers.Exists(x => x == newAnswer);
if(alreadyPicked)
{
PickedAnswers.Add(newAnswer);
}
else
{
PickedAnswers.Add(newAnswer);
}
}
}
public void EraseAnswers ()
{
PickedAnswers = new List<AnswerData>();
}
void Display()
{
EraseAnswers();
var question = GetRandomQuestion();
if (events.UpdateQuestionUI != null)
{
events.UpdateQuestionUI(question);
}
/*if(question.UseTimer)
{
UpdateTimer(question.UseTimer);
}*/
}
public void Accept()
{
UpdateTimer(false);
bool IsCorrect = CheckAnswers();
FinishedQuestions.Add(currentQuestion);
UpdateScore((IsCorrect) ? data.Questions[currentQuestion].AddScore : -data.Questions[currentQuestion].AddScore);
if(IsFinished)
{
SetHighscore();
}
var type = (IsFinished) ? UIManager.ResolutionScreenType.Finish : (IsCorrect) ? UIManager.ResolutionScreenType.Correct : UIManager.ResolutionScreenType.Incorrect;
if (events.DisplayResolutionScreen != null)
{
events.DisplayResolutionScreen(type, data.Questions[currentQuestion].AddScore);
}
if(type != UIManager.ResolutionScreenType.Finish){
if (IE_WaitTillNextRound != null)
{
StopCoroutine(IE_WaitTillNextRound);
}
IE_WaitTillNextRound = WaitTillNextRound();
StartCoroutine(IE_WaitTillNextRound);
}
}
#region Timer Methods
void UpdateTimer (bool state)
{
switch(state)
{
case true:
IE_StartTimer = StartTimer();
StartCoroutine(IE_StartTimer);
timerAnimtor.SetInteger(timerStateParaHash, 2);
break;
case false:
if(IE_StartTimer != null)
{
StopCoroutine(IE_StartTimer);
}
timerAnimtor.SetInteger(timerStateParaHash, 1);
break;
}
}
IEnumerator StartTimer()
{
var totalTime = data.Questions[currentQuestion].Timer;
var timeLeft = totalTime;
timerText.color = timerDefaultColor;
while(timeLeft > 0)
{
timeLeft--;
if(timeLeft < totalTime / 2 && timeLeft > totalTime /4)
{
timerText.color = timerHalfWayOutColor;
}
if(timeLeft < totalTime / 4)
{
timerText.color = timerAlmostOutColor;
}
timerText.text = timeLeft.ToString();
yield return new WaitForSeconds(1.0f);
}
Accept();
}
IEnumerator WaitTillNextRound()
{
yield return new WaitForSeconds(GameUtility.ResolutionDelayTime);
Display();
}
#endregion
/*Question GetRandomQuestion ()
{
var randomIndex = GetRandomQuestionIndex();
currentQuestion = randomIndex;
return data.Questions[currentQuestion];
}
int GetRandomQuestionIndex ()
{
var random = 0;
if (FinishedQuestions.Count < data.Questions.Length)
{
do
{
random = UnityEngine.Random.Range(0, data.Questions.Length);
}while (FinishedQuestions.Contains(random) || random == currentQuestion);
}
return random;
}*/
bool CheckAnswers()
{
if(!CompareAnswers())
{
return false;
}
return true;
}
bool CompareAnswers()
{
if(PickedAnswers.Count > 0)
{
List<int> c = data.Questions[currentQuestion].GetCorrectAnswers();
List<int> p = PickedAnswers.Select(x => x.AnswerIndex).ToList();
var f = c.Except(p).ToList();
var s = p.Except(c).ToList();
return !f.Any() && !s.Any();
}
return false;
}
//Function called to load data(Questions)..
void LoadData()
{
data = Data.Fetch();
}
public void RestartGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
public void QuitQuiz()
{
SceneManager.LoadScene("QuizHome");
}
private void SetHighscore()
{
var highscore = PlayerPrefs.GetInt(GameUtility.SavePrefKey);
if(highscore < events.CurrentFinalScore)
{
PlayerPrefs.SetInt(GameUtility.SavePrefKey, events.CurrentFinalScore);
}
}
private void UpdateScore(int add)
{
events.CurrentFinalScore += add;
if(events.CurrentFinalScore < 0)
{
events.CurrentFinalScore = 0;
}
events.ScoreUpdated ?.Invoke();
}
#region Getters
Question GetRandomQuestion()
{
var randomIndex = GetRandomQuestionIndex();
currentQuestion = randomIndex;
return data.Questions[currentQuestion];
}
int GetRandomQuestionIndex()
{
var random = 0;
if(FinishedQuestions.Count < data.Questions.Length)
{
do
{
random = UnityEngine.Random.Range(0, data.Questions.Length);
}
while(FinishedQuestions.Contains(random) || random == currentQuestion);
}
return random;
}
#endregion
}
Hi. You shouldn't post this as an answer; this should be an edit to your question, or at least a comment. Anyway, this is not the code we need to see. We need to see the code that calls the delegates; the code where UpdateQuestionAnswer(); is written, and the same for the other delegates. We also need to see the code in the listeners, (for example, the UpdateAnswers method).