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.
I've been trying to bind SpaceText to Space in the following way:
Is it correct way to do it and is it necessary?
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.
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.
.