Refactoring code into multiple methods breaks functionality, Please help!

I’m working through the Quiz Master project and I’ve encountered an interesting problem which I can’t solve myself.
Everything was working perfectly until I go to the point where you have to implement the Timer functionality. At that point the tutorial calls for you to refactor the code so that a lot of stuff that was happening in the Start method gets broken out into multiple methods that all call each other.
I must have done some thing wrong because my Timer works fine but the previous functionality is now broken but in a an interesting way.
The game runs but with the following error messages logging to the console:

  1. UnassignedReferenceException: The variable answerButtons of Quiz has not been assigned.
    You probably need to assign the answerButtons variable of the Quiz script in the inspector.
    UnityEngine.GameObject.GetComponent[T] () (at <4746c126b0b54f3b834845974d1a9190>:0)
    Quiz.SetButtonState (System.Boolean state) (at Assets/Scripts/Quiz.cs:83)
    Quiz.GetNextQuestion () (at Assets/Scripts/Quiz.cs:63)
    Quiz.Start () (at Assets/Scripts/Quiz.cs:26)
  2. NullReferenceException: Object reference not set to an instance of an object
    Quiz.Update () (at Assets/Scripts/Quiz.cs:31)
  3. IndexOutOfRangeException: Index was outside the bounds of the array.
    Quiz.SetButtonState (System.Boolean state) (at Assets/Scripts/Quiz.cs:83)
    Quiz.GetNextQuestion () (at Assets/Scripts/Quiz.cs:63)
    Quiz.Update () (at Assets/Scripts/Quiz.cs:34)

Error 1 only happens on Start. Errors 2 and 3 recur on Update.
The answer text does not pull through to the buttons but the buttons remain clickable and change from one sprite to the other on click as they are supposed to (I changed the code slightly to have the button clicked change colour) The state change function also seems to be broken as you can now click multiple times to change your answer.
The question/answer text above the buttons changes as it is supposed to.

I’ve been back through the video’s and can’t spot where I’ve gone wrong but I think it’s something to do with the answerButton array the game uses to find the correct scriptable object. Can anyone look over my code and tell me what I’ve done wrong?

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

public class Quiz : MonoBehaviour
{
    [Header("Questions")]
    [SerializeField] TextMeshProUGUI questionText;
    [SerializeField] QuestionSO question;
    [Header("Answers")]
    [SerializeField] GameObject[] answerButtons;
    int correctAnswerIndex;
    [Header("Button Colors")]
    [SerializeField] Sprite defaultAnswerSprite;
    [SerializeField] Sprite correctAnswerSprite;
    [Header("Timer")]
    [SerializeField] Image timerImage;
    Timer timer;
    

    void Start()
    {
        timer = FindObjectOfType<Timer>();
        GetNextQuestion(); 
    }

    void Update()
    {
        timerImage.fillAmount = timer.fillFraction;
        if(timer.loadNextQuestion)
        {
            GetNextQuestion();
            timer.loadNextQuestion = false;
        }
    }

    public void OnAnswerSelected(int index)
    {
        Image buttonImage;

        if (index == question.GetCorrectAnswerIndex())
        {
            questionText.text = "Correct!";
            buttonImage = answerButtons[index].GetComponent<Image>();
            buttonImage.sprite = correctAnswerSprite;
        }
        else
        {
            correctAnswerIndex = question.GetCorrectAnswerIndex();
            string correctAnswer = question.GetAnswer(correctAnswerIndex);
            questionText.text = "Sorry, the correct answer was;\n" + correctAnswer;
            buttonImage = answerButtons[index].GetComponent<Image>();
            buttonImage.sprite = correctAnswerSprite;
        }
        SetButtonState(false);
        timer.CancelTimer();
    }

    void GetNextQuestion()
    {
        SetButtonState(true);
        SetDefaultButtonSprites();
        DisplayQuestion();
    }

    void DisplayQuestion()
    {
        questionText.text = question.GetQuestion();

        for (int i = 0; i < answerButtons.Length; i++)
        {
            TextMeshProUGUI buttonText = answerButtons[i].GetComponentInChildren<TextMeshProUGUI>();
            buttonText.text = question.GetAnswer(i);
        }
    }

    void SetButtonState(bool state)
    {
        for(int i = 0; 1 < 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 haven’t looked through the code yet but in the mean time you can check if the buttons are still assigned in the inspector. I don’t know if you have, but when you change the variable name of something that shows in the inspector it becomes a new field and the original values are no longer filled in.

I will now look at the code


I believe the first object reference exception may be on timerImage. Make sure this is still set in the inspector.

Yup, both the first and last exceptions refer to your answerButtons not having any values. As I said, check in the inspector if those are still assigned.

Thanks for the prompt response.
The answerButton references were still assigned. Just to be sure I cleared the AnswerButton and TimerImage references and reapplied them. No effect.
I’ll just reiterate that the Timer functionality is working as expected, including the orange circle (TimerImage) reducing to zero.

OK, Let’s break it down: The second exception is a null reference exception. That is that an object you are trying to access is null. The offending line is this

timerImage.fillAmount = timer.fillFraction;

There are only two things that can be null here: timerImage or timer. You say your orange circle (timerImage) is reducing to zero. This line is what’s doing that. If it’s reducing to zero then none of these things are null. You may have a second Quiz component somewhere in your hierarchy where things are not assigned. If that is the case, you will also find that the other 2 exceptions go away when you remove this rogue Quiz component.

You can add this just above that line and check the console

Debug.Assert(timerImage != null);
Debug.Assert(timer != null);

This will cause the game to pause, or print something to the console and tell you which of these are null, if any.

Also, perhaps add Debug.Log("Quiz Start") to your Start() method. If there are two Quiz components, it will log to the console twice

having added those lines to the Start and Update methods I now have an additional error in my console:

Assertion failed
UnityEngine.Debug:Assert (bool)
Quiz:Update () (at Assets/Scripts/Quiz.cs:32)

Yeah, so if you double click on the exception it will take you to the Debug.Assert that failed. If I had to hazard a guess, it looks like it may be timer

when I double-click on the error message it highlights
Debug.Assert(timerImage != null); in my IDE

OK, So timerImage is null. That means it’s not assigned, but you say it is. This strengthens my belief that you have another Quiz component somewhere. How many times did “Quiz Start” print to the console? There will either be 2 lines, or a single line with a 2 next to it (if there are 2)

found it! There was a Quiz (script) component attached to QuestionText as well with come null references. Is it safe to just I remove that?

Yes! There should only be one Quiz component. The second one is running the same code but with unassigned references and spitting out exceptions that make us run around in circles. This should also fix the other exceptions because they are also related to unassigned references

OK, that’s one error down. The IndexOutOfRangeException is still there and now neither the question, nor the answers are pulling through

Mini Challenge Find your typo here

1 < answerButtons.Length should be i < answerButtons.Length

I used a 1 not an i? in the for loop? Well I feel stupid!

Don’t. It happens. I’ve done it. Others have done it. Should get you going on to the next lecture. Enjoy

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

Privacy & Terms