Search Unity

Question My first player turn is being skipped

Discussion in 'Getting Started' started by Gurk, Aug 19, 2023.

  1. Gurk

    Gurk

    Joined:
    Feb 4, 2015
    Posts:
    4
    Hello everyone, I'm starting to learn to do some Unity with a simple game where players change turns after selecting a card. When the turn is done, the next player get to choose the card. When the turn goes through 1 to 4 there is no problem, but when the 4th takes a card, the first player chooses automatically its card and passes the turn to the second player.

    I've being trying to figure out what is happening, but the checking I've being doing tells me that the turn is being properly set, but somehow the first player gets to choose a card without pressing any key.

    My guess is best to paste here the logic I've being doing to explain myself better. Hope the approach isn't very nonsensical.

    I have a Coordinator class that in the Update() uses this method to set the player turn, at the beginning the player turn is set to -1 to be able to choose the starting player at random:

    Code (CSharp):
    1.  
    2. private void SetPlayerTurn()
    3.     {
    4.         if (playerTurn < 0)
    5.         {
    6.             playerTurn = Utilities.GiveStartingTurn(numPlayers);
    7.             players[playerTurn].GetComponent<Player>().ToggleMyTurn();
    8.         }
    9.        //Ask if the player has chosen a card
    10.         if (players[playerTurn].GetComponent<Player>().TurnDone())
    11.         {
    12.             // Remove this players turn
    13.             players[playerTurn].GetComponent<Player>().ToggleMyTurn();
    14.             // Change the turn to the next player
    15.             playerTurn++;
    16.             // If the last player played, set it to the first one again
    17.             playerTurn = playerTurn >= numPlayers ? 0 : playerTurn;
    18.             // Give that player its turn
    19.             players[playerTurn].GetComponent<Player>().ToggleMyTurn();
    20.         }
    21.     }
    At the Player class, while the turn is set to true, the update is listening to the players input with Input.KeyDown(). From documentation I learned that once pressed it would not return true even if it still pressed in the next frames.

    Code (CSharp):
    1.  
    2. void Update()
    3.     {
    4.         if (myTurn && !turnPlayed)
    5.         {
    6.             ChooseCard();
    7.         }
    8.     }

    Code (CSharp):
    1.  
    2. private void ChooseCard()
    3.     {
    4.         if (Input.GetKeyDown("1"))
    5.         {          
    6.             turnPlayed = true;
    7.         }
    8. }
    ToggleTurn just sets if the player can choose a card or not, called when its the player's turn or when its over.

    Code (CSharp):
    1.  
    2. public void ToggleMyTurn()
    3.     {
    4.         myTurn = !myTurn;
    5.     }
    TurnDone is a method that is always being called by the coordinator to check if the player has choosen a card, to skip to the next player.

    Code (CSharp):
    1.  
    2. public bool TurnDone()
    3.     {
    4.         bool turnPlayedTemp = turnPlayed;
    5.         turnPlayed = false;
    6.         return turnPlayedTemp;
    7.     }
    ----
    When checking the player turn after choosing a card from 4th to 1st, the playerTurn is set properly to 0, but when it checks if the player has pressed a key, somehow it seems to think that it did, and so the next player takes its turn.

    upload_2023-8-19_16-10-58.png

    Maybe after posting this question I get what is happening, but I've been with this situation for some time.

    Thank you very much for your time.
     

    Attached Files:

    Last edited: Aug 19, 2023
  2. AngryProgrammer

    AngryProgrammer

    Joined:
    Jun 4, 2019
    Posts:
    490
    What I could change is not using Update to set whose turn is now. It's a main problem here because the game reset after the last player turns. Start from a simple GameManager, set inside a function that all players can use to end their turn. Your turn will end exactly when you wanted and is not dependent on the Update refresh rate.
     
    Gurk likes this.
  3. Gurk

    Gurk

    Joined:
    Feb 4, 2015
    Posts:
    4
    It seems I still don't fully understand the Update proper usage. I will try the approach you describe and see if I can manage.

    Thank you very much for your quick response!
     
  4. Gurk

    Gurk

    Joined:
    Feb 4, 2015
    Posts:
    4
    The situation just got worse. I'm not sure if I used the suggestion by AngryProgrammer properly, I would like to share the classes I'm using where I made the Coordinator a Singleton class to be able to call the PlayedCard method to change the turn only when the player chooses one.

    With this approach I see in the console that now, instead of the next player choosing the card without input, now more players execute at the same time.

    Testing, I tried throwing an Exception and somehow the exception was thrown twice.

    upload_2023-8-20_1-57-42.png

    I feel awful having to paste everything just to ask for help, but I still can not figure out why this could be happening.
    The Player Prefab is an empty object with the Player script.
    The Card Prefab us just an empty object with nothing for now.

    GameCoordinator

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. public class GameCoordinator : MonoBehaviour
    5. {
    6.     public List<string> colorDecks = new() { "R", "B", "Y", "G" };
    7.     public GameObject playerPrefab;
    8.     public GameObject cardPrefab;
    9.     public List<GameObject> players = new List<GameObject>();
    10.     public List<GameObject> deck = new List<GameObject>();
    11.     public static int maxCardsPerPlayer = 5;
    12.     public int numPlayers = 4;
    13.     public int playerTurn;
    14.  
    15.     public static GameCoordinator Instance { get; private set; }
    16.  
    17.     void Awake()
    18.     {
    19.         if (Instance == null)
    20.         {
    21.             Instance = this;
    22.         }
    23.         else if (Instance != null && Instance != this)
    24.         {
    25.             Destroy(this);
    26.         }
    27.     }
    28.  
    29.     // Start is called before the first frame update
    30.     void Start()
    31.     {
    32.         CreatePlayers();
    33.         CreateDeck();
    34.  
    35.         Utilities.SuffleDeck(deck);
    36.  
    37.         DealCards();
    38.  
    39.         playerTurn = Random.Range(0, numPlayers);
    40.         players[playerTurn].GetComponent<Player>().ToggleMyTurn(true);
    41.     }
    42.  
    43.     // Update is called once per frame
    44.     void Update()
    45.     {
    46.  
    47.     }
    48.  
    49.     private void CreatePlayers()
    50.     {
    51.         for (int i = 0; i < numPlayers; i++)
    52.         {
    53.             GameObject newPlayer = Instantiate(playerPrefab, new Vector3(0, 0, 0), Quaternion.identity);
    54.             newPlayer.name = "Player" + (i + 1);
    55.             players.Add(newPlayer);
    56.         }
    57.     }
    58.  
    59.     private void CreateDeck()
    60.     {
    61.         for (int i = 1; i <= 8; i++)
    62.         {
    63.             foreach (string deckColor in colorDecks)
    64.             {
    65.                 GameObject newCard = Instantiate(cardPrefab, new Vector3(0, 0, 0), Quaternion.identity);
    66.                 newCard.name = deckColor + i;
    67.                 deck.Add(newCard);
    68.             }
    69.         }
    70.     }
    71.  
    72.     private void DealCards()
    73.     {
    74.         foreach (GameObject currentPlayer in players)
    75.         {
    76.             for (int i = 0; i < maxCardsPerPlayer; i++)
    77.             {
    78.                 currentPlayer.GetComponent<Player>().AddCard(deck[0]);
    79.                 deck.RemoveAt(0);
    80.             }
    81.         }
    82.     }
    83.  
    84.     private void SetNextPlayersTurn()
    85.     {
    86.         players[playerTurn].GetComponent<Player>().ToggleMyTurn(false);
    87.         playerTurn++;
    88.         playerTurn = playerTurn >= numPlayers ? 0 : playerTurn;
    89.         players[playerTurn].GetComponent<Player>().ToggleMyTurn(true);
    90.         throw new System.Exception();
    91.     }
    92.  
    93.     public void PlayedCard()
    94.     {
    95.         SetNextPlayersTurn();
    96.     }
    97. }
    Player

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Player : MonoBehaviour
    6. {
    7.     [SerializeField]
    8.     private List<GameObject> cardsOnHand = new List<GameObject>();
    9.     [SerializeField]
    10.     private bool turnEnabled = false;
    11.  
    12.     // Start is called before the first frame update
    13.     void Start()
    14.     {
    15.     }
    16.  
    17.     // Update is called once per frame
    18.     void Update()
    19.     {
    20.         ChooseCard();
    21.     }
    22.  
    23.     public void ToggleMyTurn(bool status)
    24.     {
    25.         turnEnabled = status;
    26.     }
    27.  
    28.     public void AddCard(GameObject card)
    29.     {
    30.         cardsOnHand.Add(card);
    31.     }
    32.  
    33.     private void ChooseCard()
    34.     {
    35.         if (!turnEnabled)
    36.         {
    37.             return;
    38.         }
    39.  
    40.         if (Input.GetKeyDown(KeyCode.Q))
    41.         {
    42.             GameCoordinator.Instance.PlayedCard();
    43.         }
    44.         if (Input.GetKeyDown(KeyCode.W))
    45.         {        
    46.             GameCoordinator.Instance.PlayedCard();
    47.         }
    48.         if (Input.GetKeyDown(KeyCode.E))
    49.         {      
    50.             GameCoordinator.Instance.PlayedCard();
    51.         }
    52.         if (Input.GetKeyDown(KeyCode.R))
    53.         {        
    54.             GameCoordinator.Instance.PlayedCard();
    55.         }
    56.         if (Input.GetKeyDown(KeyCode.T))
    57.         {
    58.             GameCoordinator.Instance.PlayedCard();
    59.         }
    60.     }
    61. }
    62.  
    Utilities

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Utilities
    6. {
    7.     public static List<GameObject> SuffleDeck(List<GameObject> deck)
    8.     {
    9.         if (deck == null)
    10.         {
    11.             return new List<GameObject>();
    12.         }
    13.  
    14.         int totalCards = deck.Count;
    15.  
    16.         for (int i = totalCards - 1; i > 0; i--)
    17.         {
    18.             int j = UnityEngine.Random.Range(0, i);
    19.             GameObject swapTemp = deck[i];
    20.             deck[i] = deck[j];
    21.             deck[j] = swapTemp;
    22.         }
    23.  
    24.         return deck;
    25.     }
    26. }
    27.  
     
  5. Gurk

    Gurk

    Joined:
    Feb 4, 2015
    Posts:
    4
    Hello, I figured out after sleeping on it. If what I think is true, my problem was that at the same frame the player A choose a card, the coordinator enabled player B to choose, as it was still the same frame, the second player "was" still pressing that same key, so it automatically used its turn and so on at that same frame.

    I added a new variable called previous Status that should hold the previous frame turn status, and change its value if the turn variable changes, so that the next frame it actually the next players turn.

    I placed this piece of code at the beggining of the ChooseCard method and now it looks like is working perfectly.

    Code (CSharp):
    1.         if (previousStatus != turnEnabled)
    2.         {
    3.             previousStatus = turnEnabled;
    4.             return;
    5.         }
    Thank you everyone!
     
    ijmmai likes this.