Search Unity

Tic-Tac-Toe Tutorial (aka Noughts and Crosses) Q&A

Discussion in 'Community Learning & Teaching' started by Adam-Buckner, Apr 22, 2016.

  1. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
  2. GazEcc

    GazEcc

    Joined:
    Jun 3, 2014
    Posts:
    6
    hey, i think I found a bug? if i put an x in all 4 corners and o in all 4 edges (or vice versa, the final x makes the game a draw? I'm gonna do the last bit of the tutorial tomorrow, (the pick a player part) but just wondering about that.
     
  3. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    Interesting. I'll test that today and see what happens.
     
  4. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    Looking at my final project, it works as expected:

    slack-imgs.png

    Now, I'm having a hard time (without redoing the project) checking to see if there's an issue at your step... but first:

    I'd check the code in EndTurn to make sure that all the test are correct. Tho' that should trigger 4 separate lines of code and 4 possible wins (1 row, 1 column, 2 diagonals). You'd have to have 4 lines wrong to make that not a win.

    The other thing to check is to make sure that the center grid space is correctly assigned and in the correct order...

    Can you win with a diagonal, row or column that goes thru the center before the last turn?

    Can you have a win with any last place turn? EG: From this setup, starting with X, placing the last X should create another 9th turn win.

    slack-imgs.png
     
  5. GazEcc

    GazEcc

    Joined:
    Jun 3, 2014
    Posts:
    6
    It only really seems to happen as

    xox
    oxo
    xox
    (invertable)

    and only when the last square is the middle square.

    Ive included a few wins through the middle square at various stages which all seem to be on the money.

    Thanks for your reply :)

    - Gaz
    Screen Shot 2016-04-27 at 16.28.18.PNG Screen Shot 2016-04-27 at 16.28.49.PNG Screen Shot 2016-04-27 at 16.28.57.PNG
     
  6. GazEcc

    GazEcc

    Joined:
    Jun 3, 2014
    Posts:
    6
    And just trying there to get the last square winning and yeah it seems square 9 is always a draw, does that mean the draw condition >=9 is the fault? Screen Shot 2016-04-27 at 16.34.12.PNG
     
  7. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    What value do you start your move count with? And when and where do you increment it. You may inadvertently be one move ahead... Or something similar.

    Have you jumped ahead at any point?

    There is one point where the entire EndTurn gets moved from if's to an if/else block.

    ...

    Where are you in the tutorial?
     
  8. GazEcc

    GazEcc

    Joined:
    Jun 3, 2014
    Posts:
    6
    Hey there Adam, I haven't moved on any further than the second last section. here's my code :)

    (Please Ignore the comments as I put them there just to show myself why I've done x y or z in that area and they show my complete n00bness :p)
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4. using System.Collections;
    5.  
    6. [System.Serializable]
    7. //creatingaclassissomethingtolookinto
    8. //itseemsthatyoucanthenassignittoanobjectorwhateverandpullitinasyouneed?invoidsandstuff.
    9. //Theybecomelikeadropdownmenu,intheinspector?notsurewhy.
    10. public class Player
    11. {
    12.  public Image panel;
    13.  public Text text;
    14. }
    15.  
    16. [System.Serializable]
    17. public class PlayerColor
    18. {
    19.  public Color panelColor;
    20.  public Color textColor;
    21. }
    22.  
    23. public class GameController : MonoBehaviour {
    24.  //The[]meanscreateanarraythatispopulatedinthe editor
    25.  public Text[] buttonList;
    26.  //JusttheGameOvertextpaneland text
    27.  public GameObject gameOverPanel;
    28.  public Text gameOverText;
    29.  //Torestartthe game
    30.  public GameObject restartButton;
    31.  //Playerisacustomclasswecallhere...
    32.  public Player playerX;
    33.  public Player playerO;
    34.  //PlayerColourisalsoapublicclasswecallherethatwecreatedandpopulatedintheeditor.
    35.  public PlayerColor activePlayerColor;
    36.  public PlayerColor inactivePlayerColor;
    37.  private string playerSide;
    38.  //movecounterstored here
    39.  private int moveCount;
    40.  
    41.  void Awake ()
    42.  {
    43.  SetGameControllerReferenceOnButtons();
    44.  playerSide = "X";
    45.  //stopsthegameoverwindowonfirst move
    46.  gameOverPanel.SetActive(false);
    47.  //setsmovecountto0soitcanstartcountingtowardsadraw.
    48.  moveCount = 0;
    49.  //turnsoffthereset button
    50.  restartButton.SetActive(false);
    51.  //setouractiveandinactiveplayercolours.
    52.  SetPlayerColors(playerX, playerO);
    53.  }
    54.  
    55.  void SetGameControllerReferenceOnButtons ()
    56.  {
    57.  for (int i = 0; i < buttonList.Length; i++)
    58.  {
    59.  buttonList .GetComponentInParent<GridSpace>().SetGameControllerReference(this);
    60.  }
    61.  }
    62.  
    63.  public string GetPlayerSide ()
    64.  {
    65.  return playerSide;
    66.  }
    67.  
    68.  public void EndTurn ()
    69.  {
    70.  //addsanextramovetothemove counter
    71.  moveCount ++;
    72.  //checkshowmanymoveshavebeen made
    73.  if (moveCount >= 9)
    74.  {
    75.  GameOver ("draw");
    76.  }
    77.  //checkstoseeiftheHorizontallinesare complete
    78.  else if (buttonList [0].text == playerSide && buttonList [1].text == playerSide && buttonList [2].text == playerSide)
    79.  {
    80.  GameOver (playerSide);
    81.  }
    82.  else if (buttonList [3].text == playerSide && buttonList [4].text == playerSide && buttonList [5].text == playerSide)
    83.  {
    84.  GameOver (playerSide);
    85.  }
    86.  else if (buttonList [6].text == playerSide && buttonList [7].text == playerSide && buttonList [8].text == playerSide)
    87.  {
    88.  GameOver (playerSide);
    89.  }
    90.  //checkstoseeiftheverticallinesare complete
    91.  else if (buttonList [0].text == playerSide && buttonList [3].text == playerSide && buttonList [6].text == playerSide)
    92.  {
    93.  GameOver (playerSide);
    94.  }
    95.  else if (buttonList [1].text == playerSide && buttonList [4].text == playerSide && buttonList [7].text == playerSide)
    96.  {
    97.  GameOver (playerSide);
    98.  }
    99.  else if (buttonList [2].text == playerSide && buttonList [5].text == playerSide && buttonList [8].text == playerSide)
    100.  {
    101.  GameOver (playerSide);
    102.  }
    103.  //checkstoseeifthediagonallinesare complete
    104.  else if (buttonList [0].text == playerSide && buttonList [4].text == playerSide && buttonList [8].text == playerSide)
    105.  {
    106.  GameOver (playerSide);
    107.  }
    108.  else if (buttonList [2].text == playerSide && buttonList [4].text == playerSide && buttonList [6].text == playerSide)
    109.  {
    110.  GameOver (playerSide);
    111.  }
    112.  else ChangeSides ();
    113.  }
    114.  
    115.  void ChangeSides ()
    116.  {
    117.  playerSide = (playerSide == "X") ? "O" : "X";
    118.  if (playerSide == "X")
    119.  {
    120.  SetPlayerColors(playerX, playerO);
    121.  }
    122.  else
    123.  {
    124.  SetPlayerColors(playerO, playerX);
    125.  }
    126.  }
    127.  
    128.  void SetPlayerColors (Player newPlayer,Player oldPlayer)
    129.  {
    130.  newPlayer.panel.color = activePlayerColor.panelColor;
    131.  newPlayer.text.color = activePlayerColor.textColor;
    132.  oldPlayer.panel.color = inactivePlayerColor.panelColor;
    133.  oldPlayer.text.color = inactivePlayerColor.textColor;
    134.  }
    135.  
    136.  void GameOver(string winningPlayer)
    137.  {
    138.  if (winningPlayer == "draw")
    139.  {
    140.  SetGameOverText("It'saDraw!");
    141.  }
    142.  else
    143.  {
    144.  SetGameOverText(winningPlayer + "Wins!");
    145.  }
    146.  SetBoardInteractable (false);
    147.  restartButton.SetActive(true);
    148.  }
    149.  
    150.  void SetGameOverText(string value)
    151.  {
    152.  gameOverPanel.SetActive(true);
    153.  gameOverText.text = value;
    154.  }
    155.  public void RestartGame()
    156.  {
    157.  playerSide = "X";
    158.  moveCount = 0;
    159.  gameOverPanel.SetActive(false);
    160.  restartButton.SetActive(false);
    161.  SetPlayerColors(playerX, playerO);
    162.  for (int i = 0; i < buttonList.Length; i++)
    163.  {
    164.  buttonList .text = "";
    165.  }
    166.  SetBoardInteractable(true);
    167.  }
    168.  
    169.  void SetBoardInteractable (bool toggle)
    170.  {
    171.  for (int i = 0; i < buttonList.Length; i++)
    172.  {
    173.  buttonList .GetComponentInParent<Button>().interactable = toggle;
    174.  }
    175.  }
    176. }
    177.  

    Edited to fix code tags... Tried to add some new lines to make it more readable... Moderator
     
    Last edited by a moderator: Apr 10, 2017
  9. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    FWIW our code tags take square brackets: [ ]

    I would have added them, but your code has lost its formatting.

    It looks like you have the move count test first, not last. As code is executed top top bottom in a function, it reaches the move count test before the win test, so if it's turn 9, you will always get a draw.
     
  10. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    I see that you have some questions in your comments. If you still have questions, feel free to ask them.
     
  11. GazEcc

    GazEcc

    Joined:
    Jun 3, 2014
    Posts:
    6
    Hey there Adam, thanks for your help buddy :)

    And well, I'm still confused as to WHY I'm doing a lot of these things, I find it a little hard to understand WHY I use things like void, but I know void update is everytime the game refreshes or something similar, in a previous project I used fixed update which was like a regular pulse. But yeah.. what is void? :p
     
  12. GazEcc

    GazEcc

    Joined:
    Jun 3, 2014
    Posts:
    6
    And Sorry about the <> I used to do HTML, guess I just fell into an old habit :p
     
  13. Socrates

    Socrates

    Joined:
    Mar 29, 2011
    Posts:
    681
    The "void" is saying "this function returns nothing at all." For a "void" you can use "return;" or not have a return at all. For other types of returns, you set up the proper return value.

    For example:
    Code (csharp):
    1. void SomeFunction() {} // Returns nothing.
    2.  
    3. int SomeOtherFunction()
    4. {
    5.     return 5;
    6. } // Returns an int.
    7.  
    8. bool SomeTrueFunction()
    9. {
    10.     return true;
    11. }  // Always return true.
    12.  
    The Update() function is called every single frame, every time Unity draws the screen. There is some official documentation here. The FixedUpdate() is probably what you mention using before and is explained here.

    This short tutorial may help make it more clear.


    Edited to make more sense than my first post. :)
     
  14. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    What @Socrates said...

    void is a return type for a function. In C#, all functions must have a return type. In many cases, functions simply "Do Something" but are not necessarily processing data and returning a result. These are void.

    Code (csharp):
    1. void SetBoardInteractable (bool toggle)
    2. {
    3.    for (int i = 0; i < buttonList.Length; i++)
    4.    {
    5.       buttonList .GetComponentInParent<Button>().interactable = toggle;
    6.    }
    7. }
    In the above example, the function toggles all of the buttons in buttonList, but returns no value, so the return type is void.

    Code (csharp):
    1. int AddTwo (int a, int b)
    2. {
    3.    return a + b;
    4. }
    In the above example, the function processes data and returns a value, so the return type must be int.

    TYPE is the type of thing this is, in that "Is this a GameObject? Is this a Camera? Is this a float?" The type is the name of the class. If you write your own class, eg: public class GameController, the type is GameController. Don't forget that ultimately, even tho' Components are "built-in", these Components - like Cameras and Rigidbodies - are defined somewhere and they are usually classes, just like the classes you write in your scripts. They are identified by type.

    Now, C# can get a little pedantic with type.

    Code (csharp):
    1. int myNewInt = AddTwo (4, 5);
    2.  
    3. int AddTwo (int a, int b)
    4. {
    5.    int calculatedValue = a + b;
    6.    return calculatedValue;
    7. }
    We define int 3 times! 5 is you count the parameters! Shouldn't the compiler know this should be a fussing int??

    Or...

    Code (csharp):
    1. GameObject clone = Instantiate (prefab) as GameObject;
    Well, yes the compiler can figure it out but in many cases C# is pedantic by design so you are required to be absolutely clear about what you are writing. Other languages like JavaScript (or even UnityScript) are more forgiving and can try to figure out what type things are by implication.

    There are some situations now, in C#, where you can let the compiler figure out type.

    Code (csharp):
    1. var clone = Instantiate (prefab) as GameObject;
    The var her means "This is a variable of the type being returned by the right hand side of the statement" - in this case GameObject.

    In general, it's safest to make sure you write these out by hand so there is no confusion.

    [edit]

    As a side note, functions that do not return a value still do return when the function is done. Control of the game is returned to wherever it came from. If you think of the flow of logic in a game - the logic is flowing in a big loop reading all of the code on all of the relevant and active scripts and then Unity renders a frame and then the loop goes all over again.

    Code (csharp):
    1. public Text displayText;
    2.  
    3. void Update ()
    4. {
    5.    // Some code goes here - checking for input and doing things.
    6.    SetDisplay (Time.time);
    7.    // More code goes here - checking for input and doing things.
    8. }
    9.  
    10. void SetDisplay (float time)
    11. {
    12.    displayText.text = time.ToString();
    13. }

    If you have a function like Update() and in Update, you call a function like SetDisplay. They are both functions that return void. Unity's main game loop (which we don't have control of) calls Update. The control of the game is now in Update doing things. During the execution of Update, the function SetDisplay is called. The control of the game - or the code being executed - is now in SetDisplay. Unity has executed "some code" in Update, but has not yet called "more code". The control moved to SetDisplay first. Unity executes all the code in SetDisplay and then SetDisplay returns void - but it does return. It just returns empty, nothing, void - and control of the game returns back to where it came from - in this case back to Update right after the line calling SetDisplay. Unity now executes "more code" in Update. When Update is done, the function Update then returns void and control of the game is handed back to the main Unity game loop, which will go on and look for any more Update functions on any other Components and then continue on with all the other things it does.

    So - the flow of logic is handed off to different functions and then handed back to where it left off all in a linear fashion, like a mouse threading through a maze.

    For more reading, this is the order of events that Unity calls:
    http://docs.unity3d.com/Manual/ExecutionOrder.html

    Unity will call all of these events on all relevant and enabled scripts on active GameObjects and then move on the next set of events, in a big loop, until the game's application is closed.

    As a lst side note, in advanced coding, you can have what is called "multiple threads" all executing code simultaneously. This goes beyond the definition above "all code is executed in one long linear sequence" but you have to choose to do that and it's an advanced topic. Know that it exists, but ignore it for now.
     
    Socrates likes this.
  15. EASY Studios

    EASY Studios

    Joined:
    Jan 1, 2016
    Posts:
    1
    In tutorial#8 (Ending in a draw),
    if(moveCount >= 9)
    should be
    else if(moveCount >= 9)
    Because if a player wins in the 9th move, without that else it will display "It's a Draw!".
     
  16. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    At what stage of the tutorial are you? "Else If" is the final state. If you follow the tutorial and do the save and test steps, this should work as expected. If you're testing in the middle of a step, then you might have problems. Be aware that code executes from top to bottom and the order of the code is important. If you have that line above the win checks, you'll have problems.

    If you feel there is a different error, then please point out the exact step you are in when it fails, as our tests of this tutorial don't find this issue.
     
  17. Rockyeahh

    Rockyeahh

    Joined:
    May 19, 2016
    Posts:
    11
    Hey there I'm at the end of part 5 but I keep getting an error telling me on the gamecontroller script.
    The type arguments for method 'Component.GetComponentInParent<T>()' cannot be inferred from the usage. Try specifying the type arguments explicitly.

    I also get an error on the grid space script that says 'GameController' does not contain a definition for 'GetplayerSide' and no extension method 'GetplayerSide' accepting a first argument of type 'GameController' could be found (are you missing a using directive or an assembly reference?)

    I've attached pictures of both scripts. I'm thinking I've just missed something small.
     

    Attached Files:

  18. Rockyeahh

    Rockyeahh

    Joined:
    May 19, 2016
    Posts:
    11

    I fixed the Component.GetComponentInParent problem, I had missed out <GridSpace>

    but I'm still lost on the GameController problem.
     
  19. Rockyeahh

    Rockyeahh

    Joined:
    May 19, 2016
    Posts:
    11

    Haha I just forgot to use a capital P. I'm an idiot but at least it's fixed now. :)
     
  20. TiredOwl

    TiredOwl

    Joined:
    Feb 2, 2016
    Posts:
    4
    Hi,
    I'm having a problem where a ninth move win comes up as a draw. I'm at the end of part 8, "Ending in a draw". Here is my code:

    Code (CSharp):
    1. using UnityEngine.UI;
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class GameController : MonoBehaviour {
    6.  
    7.     public Text[] buttonList;
    8.     public GameObject gameOverPanel;
    9.     public Text gameOverText;
    10.  
    11.     private string playerSide;
    12.     private int moveCount;
    13.  
    14.     void Awake ()
    15.     {
    16.         SetGameControllerReferenceOnButtons ();
    17.         playerSide = "X";
    18.         gameOverPanel.SetActive (false);
    19.         moveCount = 0;
    20.     }
    21.  
    22.     void SetGameControllerReferenceOnButtons ()
    23.     {
    24.         for (int i = 0; i < buttonList.Length; i++)
    25.         {
    26.             buttonList [i].GetComponentInParent<GridSpace> ().SetGameControllerReference(this);
    27.         }
    28.     }
    29.  
    30.     public string GetPlayerSide ()
    31.     {
    32.         return playerSide;
    33.     }
    34.  
    35.     public void EndTurn ()
    36.     {
    37.         moveCount++;
    38.         if (buttonList [0].text == playerSide && buttonList [1].text == playerSide && buttonList [2].text == playerSide)
    39.         {
    40.             GameOver ();
    41.         }
    42.         if (buttonList [3].text == playerSide && buttonList [4].text == playerSide && buttonList [5].text == playerSide)
    43.         {
    44.             GameOver ();
    45.         }
    46.         if (buttonList [6].text == playerSide && buttonList [7].text == playerSide && buttonList [8].text == playerSide)
    47.         {
    48.             GameOver ();
    49.         }
    50.         if (buttonList [0].text == playerSide && buttonList [3].text == playerSide && buttonList [6].text == playerSide)
    51.         {
    52.             GameOver ();
    53.         }
    54.         if (buttonList [1].text == playerSide && buttonList [4].text == playerSide && buttonList [7].text == playerSide)
    55.         {
    56.             GameOver ();
    57.         }
    58.         if (buttonList [2].text == playerSide && buttonList [5].text == playerSide && buttonList [8].text == playerSide)
    59.         {
    60.             GameOver ();
    61.         }
    62.         if (buttonList [0].text == playerSide && buttonList [4].text == playerSide && buttonList [8].text == playerSide)
    63.         {
    64.             GameOver ();
    65.         }
    66.         if (buttonList [2].text == playerSide && buttonList [4].text == playerSide && buttonList [6].text == playerSide)
    67.         {
    68.             GameOver ();
    69.         }
    70.  
    71.         if (moveCount >= 9)
    72.         {
    73.             SetGameOverText ("It's a draw!");
    74.         }
    75.  
    76.         ChangeSides ();
    77.     }
    78.  
    79.     void ChangeSides ()
    80.     {
    81.         playerSide = (playerSide == "X") ? "O" : "X";
    82.     }
    83.  
    84.     void GameOver ()
    85.     {
    86.         for (int i = 0; i < buttonList.Length; i++)
    87.         {
    88.             buttonList [i].GetComponentInParent<Button> ().interactable = false;
    89.         }
    90.         SetGameOverText (playerSide + " Wins!");
    91.     }
    92.  
    93.     void SetGameOverText (string value)
    94.     {
    95.         gameOverPanel.SetActive (true);
    96.         gameOverText.text = value;
    97.     }
    98. }
    99.  
    The only issue I've had in testing is if X wins on the last square. Can you spot anything wrong with my code? Many thanks in advance!
     
  21. PeaceLaced

    PeaceLaced

    Joined:
    Jun 2, 2015
    Posts:
    53
    Last edited: Jul 26, 2016
  22. PeaceLaced

    PeaceLaced

    Joined:
    Jun 2, 2015
    Posts:
    53
    Page with Error: http://unity3d.com/learn/tutorials/tic-tac-toe/game-control

    Place on Page: Using + click on Windows or + click on the Mac, select only the child Text GameObjects from every Grid Space GameObject.

    Should Be: Using Ctrl + click on Windows or Command + click on the Mac...

    AND...

    Place on Page: buttonList.GetComponentInParent().SetGameControllerReference(this);

    Should Be: buttonList.GetComponentInParent<GridSpace>().SetGameControllerReference(this);

    Found under bullet: Add code to check each item in the Button List and set the GameController reference in the GridSpace component on the parent GameObject. >> It is correct in the final script review but missing in the walk through.
     
    Last edited: Jul 26, 2016
  23. Ithalendra

    Ithalendra

    Joined:
    Aug 1, 2016
    Posts:
    1
    Hi, I'm having a problem fairly early on - right after adding the first script, I can't set up all the references. I'm using Version 5.3.5f1, so I don't know if this is a problem. There's no space for dragging and dropping the 'Grid Space' and 'Text' GameObjects under the Button component, only under the script; also no Player Side property. Furthermore, 'GridSpace' is unavailable from the function drop-down menu even though I've added the GridSpace script under the Button component.

    ***PROBLEM SOLVED - I just needed to Add Component > Scripts > GridSpace and then I was able to continue normally***
     
    Last edited: Aug 3, 2016
    ballz and lordstok like this.
  24. lordstok

    lordstok

    Joined:
    Aug 3, 2016
    Posts:
    1
    I am having a similar problem as Ithalendra. I already ran through the tutorial previously with no problems I couldn't overcome (in an earlier build of unity) but while going through it a second time (using 5.3.5f1) I am now getting stuck.

    Problem 1: On Step 4 "Foundation Game Play" i encounter my first issue in the basic script. For some reason my monodevelop throws an error on "button.interactable = false" - 'Button' does not contain a definition for 'interactable' and no extension method 'interactable' accepting a first argument of type 'Button' could be found (are you missing a using directive or an assembly reference?)'.

    Problem 2: To continue i commented the offending line, and encountered my second issue. When attempting to
    • Select the Grid Space prefab in the Project Window.
    • With the Grid Space prefab selected,
      • ... drag the Grid Space prefab onto the Button property
    I cannot drag the Grid Space prefab onto the Button property. I can move the Text in to the Button Text field without an issue, but the Button field refuses to update whether I use drag, or select it manually!
     
  25. PeaceLaced

    PeaceLaced

    Joined:
    Jun 2, 2015
    Posts:
    53
    @lordstok

    Make sure you have correctly defined the button variable as Button type.

    Code (CSharp):
    1. public Button button;
     
  26. Nisa_11

    Nisa_11

    Joined:
    Nov 26, 2015
    Posts:
    1
    upload_2016-8-29_15-35-58.png

    Can someone help me? It doesnt wanna check the EndTurn();


    public void EndTurn(){

    moveCount ++;

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

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

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

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

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

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

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

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

    if (moveCount >= 9){
    SetGameOverText("It's a draw!");
    }

    ChangeSide();
    }

    I already put all the condition, but it seems like just a couple of them can run .. why is it happen?
     
  27. Phantamoss

    Phantamoss

    Joined:
    Sep 1, 2016
    Posts:
    1
    I'm not sure if it's my version of Unity or the Maker's version, but creating a script, and dragging it to OnClick, does not allow me to select the GridSpace script from the function list. I had to Add Componenet -> New Script -> GridSpace.cs
     
  28. Toomblercazz

    Toomblercazz

    Joined:
    Oct 8, 2016
    Posts:
    2
    Great tutorial! I've finished it and even added a running score and lines which go through the three winning symbols. If I knew how I would upload my version :)
     
  29. Sungold

    Sungold

    Joined:
    Jan 13, 2013
    Posts:
    8
    Great tutorial indeed!! I really learned a lot about GUI! I think I also had the double diagonal problem in which the following would return a draw.

    xox
    oxo
    xox

    I just fixed it by adding an extra boolean.
     
  30. ABHIJITsingh12345

    ABHIJITsingh12345

    Joined:
    Apr 14, 2016
    Posts:
    5

    Attached Files:

  31. MinecraftBoxGuy

    MinecraftBoxGuy

    Joined:
    Jun 26, 2016
    Posts:
    1
    I would like to know how to turn this game into multiplayer?
     
    dimkadimon likes this.
  32. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    2,478
    Hi @Adam-Buckner,

    Nice tutorial! I found a small mistake on this page; there is a line reading:

    It's the second line about the Transition: Highlighted colour and, according to the screen capture, it probably should read:

    :)
     
  33. p4ndepravity

    p4ndepravity

    Joined:
    Aug 31, 2016
    Posts:
    2
    what a great tutorial. exactly what I needed to jump-start my personal project. Thank you! These kinds of write-ups make it easy for me to recommend unity to everyone I meet that is interested in starting out as a developer (gaming or otherwise)
     
  34. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    Thank you. This type of feedback is always appreciated! I'm glad you liked it. We'll try to get you more.
     
  35. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    Ah! Good catch. I'll try to see which one is wrong: the screen cap or the text.
     
  36. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    2,478
    It's the text. ;)
     
  37. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    2,478
    I have a problem; I understand what the piece of code below does in game because I see what it does, but, I don't understand its syntax. Could someone explain it to me, please? Thank you! :)

    Here is the piece of code:

    Code (CSharp):
    1. void ChangeSides ()
    2.     {
    3.         playerSide = (playerSide == "X") ? "O" : "X";
    4.     }
     
  38. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    2,478
    @Adam-Buckner

    Hi Adam,

    Sorry about that but there is another small mistake and this one is present on at least two pages; this one: https://unity3d.com/learn/tutorials/tic-tac-toe/restarting-game?playlist=17111 and another one I don't remember.

    You must look for this piece of code:

    Code (CSharp):
    1. buttonList[i].GetComponentInParent() ...
    As it is, it doesn't work as GetComponentInParent is not specified (at least it doesn't for me with 5.5.0f3 Personal). It should read:

    Code (CSharp):
    1. buttonList[i].GetComponentInParent <Button> () ...
    Thanks for this great tutorial anyway! :)
     
  39. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    The syntax is: a variable on the left hand side is equal to the value returned from the right hand side based in the comparison done at the start of the right hand side. I. The parentheses there is a true / false comparison. The ? Indicates what type of statement or expression this is, and based on what is returned from the comparison, the value of the variable is set to either on or the other value after the ?. If the comparison returns true, the value of the variable is set to the first value, if it's false it is set to the value following the :.

    Another way to do this is:
    Code (csharp):
    1. if (playerSide == "X")
    2. {
    3.    playerSide = "O";
    4. }
    5. else
    6. {
    7.    playerSide = "X";
    8. }
     
    APSchmidt likes this.
  40. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    I'll take a look when I'm back from holidays! Thanks for spotting these.
     
    APSchmidt likes this.
  41. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    2,478
    Wow! Thank you! Is this syntax

    Code (CSharp):
    1. playerSide = (playerSide == "X") ? "O" : "X";
    much used? I had never seen it elsewhere before.

    By the way, sorry for bothering you during your holidays.
     
  42. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    2,478
    You're welcome! :)

    Have a good holiday! Happy new year! :)
     
  43. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    It's used often enough when trying to write less code. "One line says it all." (VS. the 8 lines I used above...)

    You will find, however, much opinion and debate in the shorter more compact vs longer more self explanatory arena. Lots of bloodshed and amusement. For super compact but hard to read, look into lamda expressions. We cover this briefly in the new adventure game tutorial.
     
    APSchmidt likes this.
  44. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    This is on our list to do, but it has not yet been done.
     
  45. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
  46. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    I thought this was down to code order and if the code was completed as the tutorial, there would be no issue. I'll try to test this again when I'm back. You also may need to make sure you have an else if... Check this against the sample code on the learn page.
     
  47. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    Fantastic!
     
  48. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    The script needs to be on a game object to drag it into the slot. You can drag the script onto a game object, but then you add that game object to the on click object field.
     
  49. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,670
    Double check your code. I be,I've this is because you have either an order issue or you have a simple if when you need an else if.
     
  50. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    2,478
    Thanks!

    How hard would it be to turn this game into a player vs ai/game? The player chooses X or O, plays once and the game plays the next turn, etc
     
    Last edited: Jan 4, 2017