Display Turn UI object only for the player with turn in unity netcode NGO

18 views Asked by At

I struggle to display the turnUI objects only for the player with the turn.

Objective; make turnUI objects only visible when player.HasTurn == true. and only visible on the specific player's screen who has the turn.

I'm exploring now unity Netcode NGO through a simple multiplayer card game.

The project is server authoritative and where separate game and UI logic are separated... to the best of my knowledge.

I try to activate the turnUI objects through the hasTurn attribute. However, the turn ui objects currently appear all clients screens as well as on the server. I tried different approaches, with and without ClientRpc, I couldn't managed to display the turnui for the player with the turn alone.

The flow of the hasTurn attribute:

The networkvariable and the event are a good starting point to display and update the hasTurn on all screen. I didn't manage though to find the condition to display it only on the player with the turn. I tried to use IsOwner,it didn'to help. I suppose that as a bool, hasTurn is a state and isn't owned by the Player. I also tried IsLocalPlayer.

After banging my head against the wall, I ask for help. Any input, reference will be highly appreciated.

Here is a short data flow of the hasTurn followed by full scripts, in case it is needed.

In short, when spawned all players have hasTurn false. At a certain time, PlayerManager assign a turn to a random player, thus changing the networkvariable HasTurn to true.

    public NetworkVariable<bool> HasTurn = new NetworkVariable<bool>(false);

This triggers the event in

public override void OnNetworkSpawn()
{
     playerUI = GetComponent<PlayerUI>();

     HasTurn.OnValueChanged += OnHasTurnChanged;
     
     OnHasTurnChanged(false, HasTurn.Value);
}

which then calls:

private void OnHasTurnChanged(bool oldValue, bool newValue)
{
    Debug.Log($"[OnHasTurnChanged] Player {PlayerName.Value}, oldValue: {oldValue}, newValue: {newValue}");

    // This ensures the UI update is only called for the player who owns this Player object.
    if (IsLocalPlayer)
    {
        Debug.Log($"[OnHasTurnChanged] Locally updating turn UI for {PlayerName.Value}");
        playerUI?.UpdateHasTurnUI(this);
    }
}

in playerui.cs:

public void UpdateHasTurnUI(Player player)
{
    Debug.Log($"[UpdateHasTurnUI] Attempting to update UI for Player {player.PlayerName.Value} with HasTurn: {player.HasTurn.Value}");
    if (player.HasTurn.Value && player == currentPlayer)
    {
        ActivateTurnUI(true);
    }
    else
    {
        ActivateTurnUI(false);
    }
}

private void ActivateTurnUI(bool activate)
{
    if (hasTurnIndicator != null)
    {
        hasTurnIndicator.SetActive(activate);
    }
    cardsDropdown.gameObject.SetActive(activate);
    playersDropdown.gameObject.SetActive(activate);
    guessButton.gameObject.SetActive(activate);
}

When these turn UI objects are activated they provide some data, I don't detail this as it isn't in the scope of my question.

The full scripts player and playerui.

    using Unity.Collections;
    using TMPro;
    using Unity.Netcode;
    using UnityEngine;
    using UnityEngine.UI;
    using System.Collections.Generic;
    using System.Linq;
     
    public class PlayerUI : MonoBehaviour
    {
    // Personal UI Elements
    #region Personal
    [SerializeField] private TextMeshProUGUI playerNameText;
    [SerializeField] private Image playerImage;
    [SerializeField] private TextMeshProUGUI scoreText;
    #endregion

    // Turn UI Elements
    #region Turn
    [SerializeField] private TMP_Dropdown cardsDropdown;
    [SerializeField] private TMP_Dropdown playersDropdown;
    [SerializeField] private GameObject hasTurnIndicator;
    [SerializeField] private Button guessButton;
    #endregion

    // Hand UI Elements
    #region Hand
    [SerializeField] private Transform cardDisplayTransform;
    #endregion

    //lists to retreive cards value in the guessbuttonclickhandler
    private List<Card> CardsPlayerCanAsk;
    private List<Player> PlayerToAsk;

    private List<int> playerIDs = new List<int>();
    private List<int> cardIDs = new List<int>();

    private const string DefaultImagePath = "Images/character_01";

    public Player currentPlayer;

    public void InitializePlayerUI(string playerName, string imagePath)
    {
        if (playerNameText != null)
        {
            playerNameText.text = playerName;
        }

        if (!string.IsNullOrEmpty(imagePath))
        {
            var imageSprite = Resources.Load<Sprite>(imagePath);
            if (playerImage != null && imageSprite != null)
            {
                playerImage.sprite = imageSprite;
            }
        }
    }

    public void InitializeTurnUI(Player player)
    {
        currentPlayer = player;

        // Assign CardsPlayerCanAsk and PlayerToAsk lists
        CardsPlayerCanAsk = player.CardsPlayerCanAsk;
        PlayerToAsk = player.PlayerToAsk;

        UpdatePlayersDropdown(currentPlayer.PlayerToAsk);
        UpdateCardsDropdown(currentPlayer.CardsPlayerCanAsk);


        if (!player.HasTurn.Value)
        {
            cardsDropdown.gameObject.SetActive(false);
            playersDropdown.gameObject.SetActive(false);
            guessButton.gameObject.SetActive(false);
        }
        else
        {
            cardsDropdown.gameObject.SetActive(true);
            playersDropdown.gameObject.SetActive(true);
            guessButton.gameObject.SetActive(true);
        }

    }

    public void UpdatePlayerHandUIWithIDs(List<int> cardIDs)
    {
        foreach (Transform child in cardDisplayTransform)
        {
            child.gameObject.SetActive(false);
        }
        Debug.Log("playerui UpdatePlayerHandUIWithIDs is called");
        foreach (int cardID in cardIDs)
        {
            Debug.Log("playerui UpdatePlayerHandUIWithIDs is called in the loop");
            CardUI cardUI = CardManager.Instance.FetchCardUIById(cardID);
            if (cardUI != null)
            {
                cardUI.gameObject.SetActive(true);
                cardUI.transform.SetParent(cardDisplayTransform, false);
            }
            else
            {
                Debug.LogWarning($"No CardUI found for card ID: {cardID}");
            }
        }
    }

    public void UpdateScoreUI(int score)
    {
        if (scoreText != null)
        {
            scoreText.text = "Score: " + score.ToString();
        }
    }

    public void UpdateHasTurnUI(Player player)
    {
        Debug.Log($"[UpdateHasTurnUI] Attempting to update UI for Player {player.PlayerName.Value} with HasTurn: {player.HasTurn.Value}");
        if (player.HasTurn.Value && player == currentPlayer)
        {
            ActivateTurnUI(true);
        }
        else
        {
            ActivateTurnUI(false);
        }
    }

    private void ActivateTurnUI(bool activate)
    {
        if (hasTurnIndicator != null)
        {
            hasTurnIndicator.SetActive(activate);
        }
        cardsDropdown.gameObject.SetActive(activate);
        playersDropdown.gameObject.SetActive(activate);
        guessButton.gameObject.SetActive(activate);
    }



    public void UpdatePlayersDropdown(List<Player> updatedPlayersToAsk)
    {
        Debug.Log("UpdatePlayersDropdown is called");
        if (updatedPlayersToAsk != null && updatedPlayersToAsk.Count > 0)
        {
            // Clear the current dropdown options and lists
            playersDropdown.ClearOptions();
            playerIDs.Clear();

            // Add the player names to the dropdown and store their IDs
            List<string> playerNames = updatedPlayersToAsk.Select(player =>
            {
                playerIDs.Add(player.PlayerDbId.Value);
                return player.PlayerName.Value.ToString();
            }).ToList();

            playersDropdown.AddOptions(playerNames);
        }
        else
        {
            Debug.LogWarning("Updated players list is null or empty.");
        }
    }

    public void UpdateCardsDropdown(List<Card> cards)
    {
        if (cardsDropdown != null)
        {
            cardsDropdown.ClearOptions(); // Clearing the dropdown options
            cardIDs.Clear(); // Clearing the associated IDs list

            if (cards != null)
            {
                List<string> cardNames = new List<string>();

                foreach (Card card in cards)
                {
                    cardIDs.Add(card.cardId.Value);

                    string cardName = card.cardName.Value.ToString();
                    cardNames.Add(cardName);

                    Debug.Log($"Added card name: {cardName} to dropdown options.");
                }


                Debug.Log($"Options passed to cardsDropdown: {string.Join(", ", cardNames)}");

                cardsDropdown.AddOptions(cardNames);
            }
            else
            {
                Debug.LogWarning("UpdateCardsDropdown - cards is null");
            }
        }
    }
}

public NetworkVariable<FixedString128Bytes> PlayerName = new NetworkVariable<FixedString128Bytes>();
public NetworkVariable<int> PlayerDbId = new NetworkVariable<int>();
public NetworkVariable<FixedString128Bytes> PlayerImagePath = new NetworkVariable<FixedString128Bytes>();
public NetworkVariable<int> Score = new NetworkVariable<int>(0);
public NetworkVariable<int> Result = new NetworkVariable<int>(0);
public NetworkVariable<bool> IsWinner = new NetworkVariable<bool>(false);
public NetworkVariable<bool> HasTurn = new NetworkVariable<bool>(false);

public List<Card> HandCards { get; set; } = new List<Card>();
public List<Player> PlayerToAsk { get; private set; } = new List<Player>();
public List<Card> CardsPlayerCanAsk { get; private set; } = new List<Card>();
public List<Card> Quartets { get; private set; } = new List<Card>();

public event Action OnPlayerToAskListUpdated;
public event Action OnCardsPlayerCanAskListUpdated;

private PlayerUI playerUI;

public override void OnNetworkSpawn()
{
    playerUI = GetComponent<PlayerUI>();

    if (IsServer)
    {
        Score.Value = 0;
    }

    Score.OnValueChanged += OnScoreChanged;
    HasTurn.OnValueChanged += OnHasTurnChanged;

    OnScoreChanged(0, Score.Value);
    OnHasTurnChanged(false, HasTurn.Value);
}

public void InitializePlayer(string name, int dbId, string imagePath)
{
    if (IsServer)
    {
        PlayerName.Value = name;
        PlayerDbId.Value = dbId;
        PlayerImagePath.Value = imagePath;

        UpdateServerUI(name, imagePath);
        BroadcastPlayerDbAttributes();
    }
}

private void UpdateServerUI(string playerName, string playerImagePath)
{
    if (playerUI != null)
    {
        playerUI.InitializePlayerUI(playerName, playerImagePath);
    }
}

public void BroadcastPlayerDbAttributes()
{
    if (IsServer)
    {
        UpdatePlayerDbAttributes_ClientRpc(PlayerName.Value.ToString(), PlayerImagePath.Value.ToString());
    }
}

[ClientRpc]
private void UpdatePlayerDbAttributes_ClientRpc(string playerName, string playerImagePath)
{
    if (playerUI != null)
    {
        playerUI.InitializePlayerUI(playerName, playerImagePath);
    }
}

public void AddCardToHand(Card card)
{
    if (IsServer) {
        HandCards.Add(card);
        UpdatePlayerHandUI();
        UpdateCardsPlayerCanAsk();
        CheckForQuartets();
    }
}

public void RemoveCardFromHand(Card card)
{
    if (card != null && IsServer)
    {
        Debug.Log($"Removed card {card.cardName} from player's hand.");
        UpdatePlayerHandUI();
        UpdateCardsPlayerCanAsk();
    }
}

//Update section

private void OnHasTurnChanged(bool oldValue, bool newValue)
{
    Debug.Log($"[OnHasTurnChanged] Player {PlayerName.Value}, oldValue: {oldValue}, newValue: {newValue}");
    if (IsLocalPlayer)
    {
        Debug.Log($"[OnHasTurnChanged] Locally updating turn UI for {PlayerName.Value}");
        playerUI?.UpdateHasTurnUI(this);
    }
}

public void UpdateTurnStatus(bool hasTurn)
{
    HasTurn.Value = hasTurn;
    Debug.Log($"[UpdateTurnStatus] Player {PlayerName.Value} hasTurn set to: {hasTurn}");

    if (playerUI != null && IsLocalPlayer)
    {
        Debug.Log($"[UpdateTurnStatus] Updating UI for Player {PlayerName.Value}.");
        playerUI.UpdateHasTurnUI(this);
    }
}


/*[ClientRpc]
private void UpdateTurnUI_ClientRpc(bool hasTurn)
{
    if (playerUI != null)
    {
        playerUI.UpdateHasTurnUI(hasTurn);
    }
}*/

/*public void BroadcastPlayerDbAttributes()
{
    if (IsServer)
    {
        UpdatePlayerDbAttributes_ClientRpc(PlayerName.Value.ToString(), PlayerImagePath.Value.ToString());
    }
}*/

private void OnScoreChanged(int oldValue, int newValue)
{
    // This method is called whenever Score changes
    UpdateScoreUI(newValue);
}

private void UpdateScoreUI(int score)
{
    if (playerUI != null)
    {
        playerUI.UpdateScoreUI(score);
    }
}


public void IncrementScore()
{
    Score.Value += 1;
}

public void UpdatePlayerHandUI()
{
    List<int> cardIDs = HandCards.Select(c => c.cardId.Value).ToList();
    playerUI?.UpdatePlayerHandUIWithIDs(cardIDs);
    Debug.Log($"Card UpdatePlayerHandUIWithIDs was called for player {PlayerName.Value}'s HandCards list.");
}

public void SendCardIDsToClient()
{
    if (IsServer)
    {
        int[] cardIDs = HandCards.Select(card => card.cardId.Value).ToArray();
        UpdatePlayerHandUI_ClientRpc(cardIDs, OwnerClientId);
    }
}

[ClientRpc]
private void UpdatePlayerHandUI_ClientRpc(int[] cardIDs, ulong targetClient)
{
    if (IsOwner)
    {
        playerUI?.UpdatePlayerHandUIWithIDs(cardIDs.ToList());
    }
}

public void UpdateCardsPlayerCanAsk()
{
    if (CardsPlayerCanAsk == null)
    {
        CardsPlayerCanAsk = new List<Card>();
    }
    else
    {
        CardsPlayerCanAsk.Clear();
    }

    //var allCards = CardManager.Instance.allSpawnedCards; // Make sure this is a List<Card>
    var allCardComponents = CardManager.Instance.allSpawnedCards.Select(go => go.GetComponent<Card>()).Where(c => c != null);

    foreach (var card in allCardComponents)
    {
        if (HandCards.Any(handCard => handCard.Suit.Value == card.Suit.Value) && !HandCards.Contains(card))
        {
            CardsPlayerCanAsk.Add(card);
        }
    }

    OnCardsPlayerCanAskListUpdated?.Invoke();
    Debug.Log($"Player {PlayerName.Value} can ask for {CardsPlayerCanAsk.Count} cards based on suits.");
}

public void UpdatePlayerToAskList(List<Player> allPlayers)
{
    PlayerToAsk.Clear();

    foreach (var potentialPlayer in allPlayers)
    {
        if (potentialPlayer != this)
        {
            PlayerToAsk.Add(potentialPlayer);
        }
    }

    OnPlayerToAskListUpdated?.Invoke(); // Raise the event
    Debug.Log($"Player {PlayerName.Value} has {PlayerToAsk.Count} players to ask.");
}

//utility method section:
public void CheckForQuartets()
{...}void Update()
{
    if (IsServer && Input.GetMouseButtonDown(0))
    {
        IncrementScoreTest();
    }
}

//to remove when isn't needed anymore:
public void IncrementScoreTest()
{
    Score.Value += 1;
}}

0

There are 0 answers