0

I have been following the tutorial here https://www.red-gate.com/simple-talk/development/dotnet-development/using-unity-ui-and-c-to-create-a-tic-tac-toe-game/. I have had some problems (How to drag text into list in Unity?) with Text type and string type, so I have changed these types to TextMeshProUGUI. Now, I have set up everything including a list in MainController. However, when I start a game, there is

NullReferenceException: Object reference not set to an instance of an object
MainController.Start () (at Assets/MainController.cs:20)

In line 20, I try to set up side.text = "X"; Should I change it to something else? Before my change to TextMeshProUGUI side, was of type string, however I thought of changing all types to TextMeshProUGUI. X text doesn't even show on the screen when I start the game.

enter image description here

I've been trying to bind SpaceText to Space in the following way: enter image description here Is it correct way to do it and is it necessary?

MainController screen: enter image description here

The code in MainController.cs:

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

public class MainController : MonoBehaviour
{

    public TextMeshProUGUI[] space_list;
    public GameObject game_over_panel;
    public TextMeshProUGUI game_over_text;
    public GameObject restart_button;
    private TextMeshProUGUI side;
    private int moves;
    
    void Start()
    {
        SetMainControllerReferenceForButtons();
        side.text = "X";
        game_over_panel.SetActive(false);
        moves = 0;
        restart_button.SetActive(false);
    }

    
    void SetMainControllerReferenceForButtons()
    {
        for (int i = 0; i < space_list.Length; i++){
            space_list[i].GetComponentInParent<Space>().SetControllerReference(this);
        }
    }

    public TextMeshProUGUI GetSide()
    {
        return side;
    }

    void ChangeSide()
    {
        if (side.text == "X")
        {
            side.text = "O";
        } else {
            side.text = "X";
        }
    }

    public void EndTurn()
    {
        moves++;
        if (space_list[0].text == side.text && space_list[1].text == side.text && space_list[2].text == side.text)
        {
            GameOver();
        }
        else if (space_list[3].text == side.text && space_list[4].text == side.text && space_list[5].text == side.text)
        {
            GameOver();
        }
        else if (space_list[6].text == side.text && space_list[7].text == side.text && space_list[8].text == side.text)
        {
            GameOver();
        }
        else if (space_list[0].text == side.text && space_list[3].text == side.text && space_list[6].text == side.text)
        {
            GameOver();
        }
        else if (space_list[1].text == side.text && space_list[4].text == side.text && space_list[7].text == side.text)
        {
            GameOver();
        }
        else if (space_list[2].text == side.text && space_list[5].text == side.text && space_list[8].text == side.text)
        {
            GameOver();
        }
        else if (space_list[0].text == side.text && space_list[4].text == side.text && space_list[8].text == side.text)
        {
            GameOver();
        }
        else if (space_list[2].text == side.text && space_list[4].text == side.text && space_list[6].text == side.text)
        {
            GameOver();
        }

        if (moves >= 9)
        {
            game_over_panel.SetActive(true);
            game_over_text.text = "Remis";
            restart_button.SetActive(true);
        }
        ChangeSide();
    }

    void GameOver()
    {
        game_over_panel.SetActive(true);
        game_over_text.text = side.text + " wygrywa!";
        restart_button.SetActive(true);
        for (int i = 0; i < space_list.Length; i++)
        {
            SetInteractable(false);
        }
    }

    void SetInteractable(bool setting)
    {
        for (int i = 0; i < space_list.Length; i++)
        {
            space_list[i].GetComponentInParent<Button>().interactable = setting;
        }
    }

    public void Restart()
    {
        side.text = "X";
        moves = 0;
        game_over_panel.SetActive(false);
        SetInteractable(true);
        restart_button.SetActive(false);
        for (int i = 0; i < space_list.Length; i++)
        {
            space_list[i].text = "";
        }
    }
}

The code in Space.cs:

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

public class Space : MonoBehaviour
{
    public Button button;
    public TextMeshProUGUI button_text;
    private MainController main_controller;

    public void SetControllerReference(MainController control)
    {
        main_controller = control;
    }

    public void SetSpace()
    {
        button_text = main_controller.GetSide();
        button.interactable = false;
        main_controller.EndTurn();
    }
}

Edit: I have changed the code in MainController.cs. Changed type of side to string and activated buttons in Start() function. Now they are clickable and value on them can be seen if I hardcode it in an Inspector. Still I don't know how to make the "X" or "O" visible after a click on a button (SetSpace is called). The NullReferenceException still appears.

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

public class MainController : MonoBehaviour
{

    public TextMeshProUGUI[] space_list;
    public GameObject game_over_panel;
    public TextMeshProUGUI game_over_text;
    public GameObject restart_button;
    private string side;
    private int moves;
    
    void Start()
    {
        SetMainControllerReferenceForButtons();
        side = "X";
        SetInteractable(true);
        game_over_panel.SetActive(false);
        moves = 0;
        restart_button.SetActive(false);
    }

    
    void SetMainControllerReferenceForButtons()
    {
        for (int i = 0; i < space_list.Length; i++){
            space_list[i].GetComponentInParent<Space>().SetControllerReference(this);
        }
    }

    public string GetSide()
    {
        return side;
    }

    void ChangeSide()
    {
        if (side == "X")
        {
            side = "O";
        } else {
            side = "X";
        }
    }

    public void EndTurn()
    {
        moves++;
        if (space_list[0].text == side && space_list[1].text == side && space_list[2].text == side)
        {
            GameOver();
        }
        else if (space_list[3].text == side && space_list[4].text == side && space_list[5].text == side)
        {
            GameOver();
        }
        else if (space_list[6].text == side && space_list[7].text == side && space_list[8].text == side)
        {
            GameOver();
        }
        else if (space_list[0].text == side && space_list[3].text == side && space_list[6].text == side)
        {
            GameOver();
        }
        else if (space_list[1].text == side && space_list[4].text == side && space_list[7].text == side)
        {
            GameOver();
        }
        else if (space_list[2].text == side && space_list[5].text == side && space_list[8].text == side)
        {
            GameOver();
        }
        else if (space_list[0].text == side && space_list[4].text == side && space_list[8].text == side)
        {
            GameOver();
        }
        else if (space_list[2].text == side && space_list[4].text == side && space_list[6].text == side)
        {
            GameOver();
        }

        if (moves >= 9)
        {
            game_over_panel.SetActive(true);
            game_over_text.text = "Remis";
            restart_button.SetActive(true);
        }
        ChangeSide();
    }

    void GameOver()
    {
        game_over_panel.SetActive(true);
        game_over_text.text = side + " wygrywa!";
        restart_button.SetActive(true);
        for (int i = 0; i < space_list.Length; i++)
        {
            SetInteractable(false);
        }
    }

    void SetInteractable(bool setting)
    {
        for (int i = 0; i < space_list.Length; i++)
        {
            space_list[i].GetComponentInParent<Button>().interactable = setting;
        }
    }

    public void Restart()
    {
        side = "X";
        moves = 0;
        game_over_panel.SetActive(false);
        SetInteractable(true);
        restart_button.SetActive(false);
        for (int i = 0; i < space_list.Length; i++)
        {
            space_list[i].text = "";
        }
    }
}

Edit 2: I have added [SerializeField] at the top of my variable side. Now in the Inspector, I have typed in "X" to make sure side has a value. It didn't solve the issue. enter image description here

Edit 3: Now when I click some of the buttons, "X" and "O" are visible, but some buttons are clickable, but nothing shows on them even though I checked whether list in MainController is full and Space(script) fields, which is Button and Button_text are entered and everything is correct here. What can be the issue? With white buttons, when I click on them there is NullReferenceException and line button_text.text = main_controller.GetSide(); is causing it. . enter image description here

code_rushh
  • 37
  • 1
  • 8

2 Answers2

0

You first need to get the TextMeshProUGUI component from the Gameobject. A solution could be something like GameObject.Find("SpaceText").GetComponent<TextMeshProUGUI>().text = "Your Text".

But according to your Picture you have multiple objects named "SpaceText" so propably you should rather do it with a GameObject variable where you then insert the wished gameobject per drag and drop in the editor.

Hope it helps :)

TryZ
  • 46
  • 6
  • SpaceText (which should appear on the Space button) is of type TextMeshPro. I have a variable "button_text" of type TextMeshProUGUI. When I change it to TextMeshPro, SpaceText can not be dragged into script Space.cs which is in Space button. – code_rushh Sep 01 '22 at 11:33
  • 1
    This might help to understand the difference between TextMeshPro vs TextMeshProUGUI https://answers.unity.com/questions/1566688/differences-between-textmeshpro-and-textmeshprougu.html – TryZ Sep 01 '22 at 11:42
0

Should I change it to something else?

Don't be afraid of error compilers give you. It doesn't mean, the stuff you are using must be faulty, but it indicates what's the problem.

And the problem is, that something is null in line 20. Go from left to right and check what might be null. Starting from side: TextMeshProUGUI is a reference type so it might be. Since side is private and not marked as [SerializeField] I know you didn't initialized it in inspector. Neither can I see you initializing it in your code.

Initialize your side variable from code or inspector to fix this.

Also you can't see X in your game as you tried assign it to null.

In the future, you might use this to resolve other nullref issues:

What is a NullReferenceException, and how do I fix it?

bartol44
  • 562
  • 2
  • 12
  • It should be initialized when Start() is called and assigned to X and it doesn't happen. Buttons aren't clickable. – code_rushh Sep 01 '22 at 10:10
  • Maybe it should, but I can't see it being done in your code. Which line do you think accomplish this? You didn't initialize it during declaration, you didn't initialize it elsewhere in your code, and you can't initialize it from inspector since you can't see it there. – bartol44 Sep 01 '22 at 10:48
  • Now I SetInteractable(true) in Start function, and if I hardcode initial value here, it can be seen and is clickable. However, on click, X or O should appear, depending on a turn, and it doesn't happen. – code_rushh Sep 01 '22 at 11:36
  • You can add whatever you want, but anything you add after your assignment to side.text won't be called, as you will get nullref and program will exit method prematurely. Underlying issue is the nullref exception, and you still didn't fix it nor proved you actually initialized side. – bartol44 Sep 01 '22 at 11:43
  • If you don't believe me, just delete this assignment for a second, a see that's the root of your issues... – bartol44 Sep 01 '22 at 11:44
  • when I delete it, the same error occurs in main_controller.EndTurn(); – code_rushh Sep 01 '22 at 11:48
  • Of course it does, because your side is still null. Read through my answer and comments again please. Where do you specify side = GetComponent() ? Where do you set side = myReferenceToTextMeshPro? Nowhere. TextMeshProUGUI is a reference type it must be initialized. – bartol44 Sep 01 '22 at 11:50
  • In the other words, you didn't tell unity that your side variable is equal to the TextMeshProUGUI inside your scene – bartol44 Sep 01 '22 at 11:51