Win conditions and taking turns

확인 완료한 버전: 5.3

-

난이도: 초급

We need to test the grid spaces on the board and see if there has been a win. We don't have the player sides properly set up, but we will need to check the grid spaces' Text property to see if the string values match "three in a row" and that these values match the side that's currently playing. To do this we will need a variable to hold the character representing the current side that is actively playing. This variable will eventually change as we alternate turns, but for now it will be a constant value. We will need to set this value when the game starts and we will need to return this value in GetPlayerSide rather than "?".

  • Open the GameController script for editing.
  • Define a private string variable called "playerSide":
private string playerSide;
  • In Awake, set playerSide to "X".
 playerSide = "X";
  • In GetPlayerSide, return playerSide rather than "?";
return playerSide;

This will now send the playerSide to GridSpace when it calls GetPlayerSide, and this sets us up for sending the current player's side when we alternate the sides.

The next step is to check for a win.

  • In EndTurn, remove the Debug.Log line.
Debug.Log("End Turn is not implemented");

To check for a win, we need to look at the three rows, the three columns and the two diagonals on our board to see if all three of the grid spaces in any series all match.

This game is small enough to test the winning conditions using brute force. We can simply check a row, comparing the three spaces with the current player’s value to see if all three spaces match the current player.

This line of code will need to go into the EndTurn function, so when we call EndTurn, we check to see if the current player has won the game. This will be done by creating a rather complex if statement. We will be checking if button0 equals playerSide and if button1 equals playerSide and if button2 equals playerSide. If all of these prove true, we have a win.

  • Add code to check the first row to the function EndTurn:
if (buttonList [0].text == playerSide && buttonList [1].text == playerSide && buttonList [2].text == playerSide)
{

}

In this line of code we are checking the top row to see if all three spaces match the current player. This is why we need to know exactly which buttons are in what spaces. We know that the grid spaces are in order, left to right and top to bottom, so by checking the text values from buttonList [0], buttonList [1] and buttonList [2] we will be checking the values for the top row:

description

Now, if a row, column or diagonal does test true for a win then we need to do a few things to end the game. In the very least we need to disable all of the buttons on the board so that the game cannot continue and not one can click any unused buttons.

So, when the win conditions are met, let’s call a function where we can do all of our "Game Over" logic.

  • Add a function that returns void called "GameOver":
void GameOver ()
{

}

The first action we would want to take when the game is over would be to turn off all of the unused buttons. To do this, we need to access each button. We can do this through our buttonList. There is no need to actually test each button to see if it is interactable or not. This is a waste of resources when running and time when coding. We can simply set all of the buttons to non-interactable by looping through all of the elements in our buttonList. If that button is already non-interactable due to a move already taken, it will stay non-interactable. The property will simply be set to the same non-interactable value. This is a little like running a hand down a row of switches and shutting them all off. Whether they were on or off to begin with is irrelevant.

  • In the GameOver function, create a new for loop that iterates through all of the elements in our buttonList.
for (int i = 0; i < buttonList.Length; i++)
{

}
  • In the for loop, for each element in the list, find the parent GameObject’s Button *component and *disable the button by making it non-interactable.
buttonList[i].GetComponentInParent().interactable = false;

Now, we need to call GameOver when the winning conditions have been met to execute this code.

  • Add a call to GameOver in the if statement in EndTurn.
GameOver();

The final script should look like this:

GameController

Code snippet

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class GameController : MonoBehaviour {

    public Text[] buttonList;

    private string playerSide;

    void Awake ()
    {
        SetGameControllerReferenceOnButtons();
        playerSide = "X";
    }

    void SetGameControllerReferenceOnButtons ()
    {
        for (int i = 0; i &lt; buttonList.Length; i++)
        {
            buttonList[i].GetComponentInParent().SetGameControllerReference(this);

        }
    }

    public string GetPlayerSide ()
    {
        return playerSide;
    }

    public void EndTurn ()
    {
        if (buttonList [0].text == playerSide &amp;&amp; buttonList [1].text == playerSide &amp;&amp; buttonList [2].text == playerSide)
        {
            GameOver();
        }
    }

    void GameOver ()
    {
        for (int i = 0; i &lt; buttonList.Length; i++)
        {
            buttonList[i].GetComponentInParent().interactable = false;
        }
    }
}
  • Save the script.
  • Return to Unity.
  • Enter Play Mode.
  • Test by clicking the top row of spaces.

When we click all three spaces in the top row, our code should detect a win. At this point, all of the rest of the buttons should be disabled. Currently, if we try to "win" by clicking any of the other rows, columns or diagonals, the game will not detect a win, obviously, but we do know that our base code is working as expected and doing what we want to do.

We now need to check the remaining rows, columns and diagonals.

  • Open the GameController script for editing.

We already have code that checks the first row. To do so, the code checks the following elements:

buttonList [0], buttonList [1] and buttonList [2]

We now need to check the rest of the possible combinations.

First the remaining rows:

buttonList [3], buttonList [4] and buttonList [5]
buttonList [6], buttonList [7] and buttonList [8]

... followed by the columns:

buttonList [0], buttonList [3] and buttonList [6]
buttonList [1], buttonList [4] and buttonList [7]
buttonList [2], buttonList [5] and buttonList [8]

... and lastly, the two diagonals:

buttonList [0], buttonList [4] and buttonList [8]
buttonList [2], buttonList [4] and buttonList [6]

description

  • Duplicate the code checking the first row 7 times, so we have a total of 8 copies; one if statement for each column, row and diagonal.
if (buttonList [0].text == playerSide && buttonList [1].text == playerSide && buttonList [2].text == playerSide)
{
    GameOver();
}
  • Change the index value, [ ], for each row, column, row and diagonal so that each if statement checks a unique row, column, row or diagonal. (See list of columns, rows and diagonals above.) For example, here is the line checking the second row:
if (buttonList [3].text == playerSide && buttonList [4].text == playerSide && buttonList [5].text == playerSide)
{
    GameOver();
}

The final script should look like this:

GameController

Code snippet

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class GameController : MonoBehaviour {

    public Text[] buttonList;

    private string playerSide;

    void Awake ()
    {
        SetGameControllerReferenceOnButtons();
        playerSide = "X";
    }

    void SetGameControllerReferenceOnButtons ()
    {
        for (int i = 0; i &lt; buttonList.Length; i++)
        {
            buttonList[i].GetComponentInParent().SetGameControllerReference(this);
        }
    }

    public string GetPlayerSide ()
    {
        return playerSide;
    }

    public void EndTurn ()
    {
        if (buttonList [0].text == playerSide &amp;&amp; buttonList [1].text == playerSide &amp;&amp; buttonList [2].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [3].text == playerSide &amp;&amp; buttonList [4].text == playerSide &amp;&amp; buttonList [5].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [6].text == playerSide &amp;&amp; buttonList [7].text == playerSide &amp;&amp; buttonList [8].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [0].text == playerSide &amp;&amp; buttonList [3].text == playerSide &amp;&amp; buttonList [6].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [1].text == playerSide &amp;&amp; buttonList [4].text == playerSide &amp;&amp; buttonList [7].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [2].text == playerSide &amp;&amp; buttonList [5].text == playerSide &amp;&amp; buttonList [8].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [0].text == playerSide &amp;&amp; buttonList [4].text == playerSide &amp;&amp; buttonList [8].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [2].text == playerSide &amp;&amp; buttonList [4].text == playerSide &amp;&amp; buttonList [6].text == playerSide)
        {
            GameOver();
        }
    }

    void GameOver ()
    {
        for (int i = 0; i &lt; buttonList.Length; i++)
        {
            buttonList[i].GetComponentInParent().interactable = false;
        }
    }
}
  • Save the script.
  • Return to Unity.
  • Enter Play Mode.
  • Test by clicking any of the spaces.

Now, all possible winning conditions should work and when the winning conditions have been met, the board should be disabled.

Next, we need to change sides when a turn is done and actually be able to play against another person.

Currently we don't really have a game. We can check for win conditions and when we get three "X"s in a row, the game is over. We now need to be able to change sides. To change sides, we will need to check what side we are on and alternate the player values; swapping "X" for "O" or vice versa.

To do this, let’s create a function that will change the player sides.

  • Create a new function that returns void called "ChangeSides".
void ChangeSides ()
{

}

In that function, we need to test our current side and swap the player’s team.

  • Add code to check the playerSide value and assign the other team’s value to playerSide.
playerSide = (playerSide == "X") ? "O" : "X";    // Note: Capital Letters for "X" and "O"

At the end of every turn we need to change sides, so let’s put a call to ChangeSides at the end of the EndTurn function.

  • Add a call to ChangeSides to the end of EndTurn:
ChangeSides();

The final script should look like this:

GameController

Code snippet

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class GameController : MonoBehaviour {

    public Text[] buttonList;

    private string playerSide;

    void Awake ()
    {
        SetGameControllerReferenceOnButtons();
        playerSide = "X";
    }

    void SetGameControllerReferenceOnButtons ()
    {
        for (int i = 0; i &lt; buttonList.Length; i++)
        {
            buttonList[i].GetComponentInParent().SetGameControllerReference(this);
        }
    }

    public string GetPlayerSide ()
    {
        return playerSide;
    }

    public void EndTurn ()
    {

        if (buttonList [0].text == playerSide &amp;&amp; buttonList [1].text == playerSide &amp;&amp; buttonList [2].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [3].text == playerSide &amp;&amp; buttonList [4].text == playerSide &amp;&amp; buttonList [5].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [6].text == playerSide &amp;&amp; buttonList [7].text == playerSide &amp;&amp; buttonList [8].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [0].text == playerSide &amp;&amp; buttonList [3].text == playerSide &amp;&amp; buttonList [6].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [1].text == playerSide &amp;&amp; buttonList [4].text == playerSide &amp;&amp; buttonList [7].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [2].text == playerSide &amp;&amp; buttonList [5].text == playerSide &amp;&amp; buttonList [8].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [0].text == playerSide &amp;&amp; buttonList [4].text == playerSide &amp;&amp; buttonList [8].text == playerSide)
        {
            GameOver();
        }

        if (buttonList [2].text == playerSide &amp;&amp; buttonList [4].text == playerSide &amp;&amp; buttonList [6].text == playerSide)
        {
            GameOver();
        }

        ChangeSides();

    }

    void ChangeSides ()
    {
        playerSide = (playerSide == "X") ? "O" : "X";
    }

    void GameOver ()
    {
        for (int i = 0; i &lt; buttonList.Length; i++)
        {
            buttonList[i].GetComponentInParent().interactable = false;
        }
    }
}
  • Save the script.
  • Return to Unity.
  • Enter Play Mode.
  • Test by clicking any of the spaces.

Now, when we click on any space, the turns alternate between "X" and "O", and if we get three "X"s or three "O"s in a row, we fulfill the win conditions and the game is over.

Our game is essentially done! At this stage, we can actually play Tic-Tac-Toe. The next step, however, is to polish the project into a proper game that people feel is finished and worth playing.