Problem with QuizMaster

I have a problem in my QuizMaster game from the “Complete C# Unity Game Developer 2D” course, I’ve followed the videos and for some reason in my game, after I press “Play again?” button, my game starts from question number 2 (as if 1st question was already answered) that way, I have only 4 questions rather than 5.

I added Debug.Log(questions.Count) at GetRandomQuestion method to see if for some reason the questions list starts with 4, and you can see that Debug.Log runs twice at the same second so it “skips” a question.
I don’t know why is that, it doesn’t seem to happen in the videos.
Don’t know even what should I share from code.
QuizMaster

Hi @Yoad! Welcome to the community.

Could you share some code? It would make it easier to maybe spot the issue

This is my Quiz.cs code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;

public class Quiz : MonoBehaviour

{

[Header("Questions")]
[SerializeField] TextMeshProUGUI questionText;
[SerializeField] List<QuestionSO> questions = new List<QuestionSO>();
QuestionSO currentQuestion;

[Header("Answers")]
[SerializeField] GameObject[] answerButtons;
int correctAnswerIndex;
bool hasAnsweredEarly = true;

[Header("Button Colors")]
[SerializeField] Sprite defaultAnswerSprite;
[SerializeField] Sprite correctAnswerSprite;

[Header("Timer")]
[SerializeField] Image timerImage;
Timer timer;

[Header("Scoring")]
[SerializeField] TextMeshProUGUI scoreText;
ScoreKeeper scoreKeeper;

[Header("ProgressBar")]
[SerializeField] Slider progressBar;
public bool isComplete;

void Awake()
{
    timer = FindObjectOfType<Timer>();
    scoreKeeper = FindObjectOfType<ScoreKeeper>();
    progressBar.maxValue = questions.Count;
    progressBar.value = 0;
}

void Update()
{
    timerImage.fillAmount = timer.fillFraction;
    if (timer.loadNextQuestion) {
        if (progressBar.value == progressBar.maxValue) {
            isComplete = true;
            return;
        }
        hasAnsweredEarly = false;
        GetNextQuestion();
        timer.loadNextQuestion = false;
    }

    else if (!hasAnsweredEarly && !timer.isAnsweringQuestion) {
        DisplayAnswer(-1);
        SetButtonState(false);
    }
}

public void OnAnswerSelected(int index) {
    hasAnsweredEarly = true;
    DisplayAnswer(index);
    SetButtonState(false);
    timer.CancelTimer();
    scoreText.text = "Score: " + scoreKeeper.CalculateScore() + "%";
}

void DisplayAnswer(int index) {
    Image buttonImage;
    if (index == currentQuestion.GetCorrectAnswerIndex()) {
        questionText.text = "Correct";
        buttonImage = answerButtons[index].GetComponent<Image>();
        buttonImage.sprite = correctAnswerSprite;
        scoreKeeper.IncrementCorrectAnswers();
    }
    else {
        correctAnswerIndex = currentQuestion.GetCorrectAnswerIndex();
        string correctAnswer = currentQuestion.GetAnswer(correctAnswerIndex);
        questionText.text = "Wrong answer, the correct answer was:\n" + correctAnswer;
        buttonImage = answerButtons[correctAnswerIndex].GetComponent<Image>();
        buttonImage.sprite = correctAnswerSprite;
    }
}

void GetNextQuestion() {
    if (questions.Count > 0) {
        SetButtonState(true);
        SetDefaultButtonSprites();
        GetRandomQuestion();
        DisplayQuestion();
        progressBar.value++;
        scoreKeeper.IncrementQuestionsSeen();
    }
}

void GetRandomQuestion() {
    Debug.Log(questions.Count);
    int index = Random.Range(0, questions.Count);
    currentQuestion = questions[index];
    if (questions.Contains(currentQuestion)) {
        questions.Remove(currentQuestion);
    }
}

void DisplayQuestion() {
    questionText.text = currentQuestion.GetQuestion();
    for (int i = 0; i < answerButtons.Length; i++) {
        TextMeshProUGUI buttonText = answerButtons[i].GetComponentInChildren<TextMeshProUGUI>();
        buttonText.text = currentQuestion.GetAnswer(i);
    }
}

void SetButtonState(bool state) {
    for (int i = 0; i < answerButtons.Length; i++) {
        Button button = answerButtons[i].GetComponent<Button>();
        button.interactable = state;
    }
}

void SetDefaultButtonSprites() {
    for (int i = 0; i < answerButtons.Length; i++) {
        Image buttonImage = answerButtons[i].GetComponent<Image>();
        buttonImage.sprite = defaultAnswerSprite;
    }
}

}

I don’t understand why GetNextQuestion() is being called twice after I start a new game which happens in my GameManager.cs

public void OnReplayLevel() {
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}

Not sure if this is the problem, but you are defaulting this to true so when this script loads, it thinks the first question was answered early. I think. Perhaps default it to false and check again

I’ve tried and it didn’t change the outcome, I was really hoping that was really the case.
The thing is, it completely skips the first question, not only the “allow answering” part.

I can’t see anything in the code you posted. Is there any way you could upload the project somewhere, then I can take a look?

First time uploading a Unity / C# project to GitHub, this is the link:

If I’m missing something or added things that are not needed, would appreciate to know what I’ve done wrong.

Thanks, I’ll take a quick look

You have a race condition between the Quiz script and the Timer script

The Update method in Quiz checks timer.loadNextQuestion and then loads the next question. It then sets timer.loadNextQuestion to false.

The Timer on the other hand sees that its timerValue is less than 0 (it defaults to 0 when the scene starts and is then decremented by Time.deltaTime) and the player is not currently answering a question, so it sets loadNextQuestion back to true

Give me a few minutes and I’ll let you know what to do to fix it

The problem is that the Quiz update method runs before the Timer update. The simplest way I found to fix it is to change the Quiz script’s update a little

In the first if, add a check to isAnsweringQuestion

if (timer.loadNextQuestion && timer.isAnsweringQuestion) // <- here
{
...
}

I think this is ok. I tested it and it looks fine. When the update runs, the Timer hasn’t set itself ‘ready’ yet, so the Quiz script does nothing. Once the Timer has run it’s first update, all will be good to go

Thanks a lot! Didn’t think about the race condition between those 2 script files.

It is working great now!

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms