Sync Var Hook Not Syncing Over The Network

I have been stuck on a problem for days on sync var hooks. I am working on displaying a player name over the players head, and I will be looking to be able to set a player colour too in the future. Here is my code.

First the sync var:

[SyncVar(hook = "OnNameChanged")] public string playerName = "";

The OnNameChanged:

        public void OnNameChanged(string _name)
    {
        playerName = _name;
        GetComponentInChildren<Text>(true).text = playerName;

    }

And Finally the OnStartClient:

The OnStartClient:
public override void OnStartClient()

    {
        playerName = "BOb";
        string _netID = GetComponent<NetworkIdentity>().netId.ToString();
        Player _player = GetComponent<Player>();





        GameManager.RegisterPlayer(_netID, _player, "Player");



    }

SyncVars only propagate to the other clients automatically if they’re changed on the server, so when a client connects you need to send the server the name it wants to use and then the server can set the SyncVar so all the other clients get it.

Didnt I do that in OnStartClient() by setting the name to BOb?

No, you would set it in OnStartServer() and read it in OnStartClient().

What does read it mean? and thank you, you have been a great help in many of my forums posts jos-yule. I appreciate it.

Sketch example

[SyncVar(hook="PlayerNameSyncCallback")]
protected string playerName = "";

public UI.Text playerNameTextField;

override OnStartServer()
{
    playerName = GetNewPlayerName();
    SetPlayerName();
}

override OnStartClient()
{
    SetPlayerName();
}

void PlayerNameSyncCallback(string newName)
{
    playerName = newName;
    SetPlayerName();
}

void SetPlayerName()
{
    playerNameTextField.text = playerName;
}
1 Like

Thanks so much!!! It finally works :slight_smile: yay

Actually, I tried it out on my brother’s computer but we encountered that player names do not work on two computers but only on 2 instances running on the same computer. I suspect its something to do with player prefs. How can I fix this?

Honestly that would require a lot more info about how you have the rest of your game setup. I should note that in your example above you hard-code the name in the code itself, which would mean that all players have the same name… Good luck!

I switched it so you can set your own name using an input field, and saves to player prefs. If I posted my entire script that has everything to do with the name on it, would you might be able to figure it out?

Here is my full script.

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


[RequireComponent(typeof(Player))]
public class PlayerSetup : NetworkBehaviour
{



    [SerializeField]
    Behaviour[] componentsToDisable;
    [SerializeField]
    GameObject[] gameObjectsToDisable;
    [SerializeField]
    string remoteLayerName = "RemotePlayer";

    [SerializeField]
    string dontDrawLayerName = "DontDraw";
    [SerializeField]
    GameObject PlayerGraphics;

    [SerializeField]
    GameObject playerUIPrefab;

    [HideInInspector]
    public GameObject playerUIInstance;
    public GameObject[] ArmObjects;

    GameObject networkManagerObject;

    [SyncVar(hook = "OnColourChanged")]
    public int playerColourIndex;
    public Text nameText;

    public Material currentPlayerMaterial = null;


    public Material[] playerMaterials;
    public GameObject[] playerObjectsWithMaterials;
    [SyncVar(hook = "OnNameChanged")] public string playerName = "";





    // Use this for initialization
    void Start()
    {


       




        if (!isLocalPlayer)
        {

            DisableComponents();
            AssignRemoteLayer();


        }

        if (isLocalPlayer)
        {


            DisableGameObjects();

            SetLayerRecursively(PlayerGraphics, LayerMask.NameToLayer(dontDrawLayerName));

            SetArmLayerRecursively();

            playerUIInstance = Instantiate(playerUIPrefab);
            playerUIInstance.name = playerUIPrefab.name;

            PlayerUI ui = playerUIInstance.GetComponent<PlayerUI>();
            if (ui == null)
                Debug.LogError("NoPlayerUI");
            ui.SetController(GetComponent<PlayerController>());

            GetComponent<Player>().SetupPlayer();

        }


    }





    void SetLayerRecursively(GameObject obj, int newLayer)
    {

        obj.layer = newLayer;

        foreach (Transform child in obj.transform)
        {

            SetLayerRecursively(child.gameObject, newLayer);

        }

    }
    void SetArmLayerRecursively()
    {



        for (int i = 0; i < ArmObjects.Length; i++)
        {
            Util.SetLayerRecursively(ArmObjects[i], LayerMask.NameToLayer("WeaponLayer"));

        }

    }


    public void SetPlayerColour(int _colour)
    {
        switch (_colour)
        {


            case 0:
                currentPlayerMaterial = playerMaterials[0];
                break;
            case 1:
                currentPlayerMaterial = playerMaterials[1];
                break;
            case 2:
                currentPlayerMaterial = playerMaterials[2];
                break;
            case 3:
                currentPlayerMaterial = playerMaterials[3];
                break;
            case 4:
                currentPlayerMaterial = playerMaterials[4];
                break;
            case 5:
                currentPlayerMaterial = playerMaterials[5];
                break;
            case 6:
                currentPlayerMaterial = playerMaterials[6];
                break;
            case 7:
                currentPlayerMaterial = playerMaterials[7];
                break;
            case 8:
                currentPlayerMaterial = playerMaterials[8];
                break;
            case 9:
                currentPlayerMaterial = playerMaterials[9];
                break;
            case 10:
                currentPlayerMaterial = playerMaterials[10];
                break;



        }

        for (int i = 0; i < playerObjectsWithMaterials.Length; i++)
        {

            playerObjectsWithMaterials[i].GetComponent<Renderer>().material = currentPlayerMaterial;

        }
    }


    public void OnColourChanged(int _colour)
    {

        playerColourIndex = _colour;
        SetPlayerColour(playerColourIndex);

    }

    public void OnNameChanged(string _name)
    {
     
        playerName = _name;
        SetPlayerName();
       

    }

    void SetPlayerName()
    {
        GetComponentInChildren<Text>(true).text = playerName;
    }



    public override void OnStartServer()

    {
        playerColourIndex = PlayerPrefs.GetInt("playerColour");

        playerName = PlayerPrefs.GetString("playerName");
    }


    public override void OnStartClient()

    {
       
        SetPlayerName();        
        SetPlayerColour(playerColourIndex);
     
       

        string _netID = GetComponent<NetworkIdentity>().netId.ToString();
        Player _player = GetComponent<Player>();





        GameManager.RegisterPlayer(_netID, _player, playerName);



    }

   



    void AssignRemoteLayer()
    {


        gameObject.layer = LayerMask.NameToLayer(remoteLayerName);
    }
    void DisableComponents()
    {
       

        for (int i = 0; i < componentsToDisable.Length; i++)
        {
            componentsToDisable[i].enabled = false;
        }

    }



    void DisableGameObjects()
    {
        for (int i = 0; i < gameObjectsToDisable.Length; i++)
        {
            gameObjectsToDisable[i].SetActive(false);
        }
    }
    // Update is called once per frame
    void Update()
    {

     
    }
    void OnDisable()
    {
        Destroy(playerUIInstance);


        if (isLocalPlayer)
            GameManager.instance.SetSceneCameraActive(true);

        GameManager.UnRegisterPlayer(transform.name);

    }

}

Nothing jumps out, but i’m also not going through with a fine toothed comb.

However, what the heck with the SetPlayerColour method?! Why not just have

SetPlayerColour(int _colour)
{
    currentPlayerMaterial = playerMaterials[_colour];
}

Why the huge case statement?

I highly suggest doing some sequence diagrams to understand the flow of messages between the Server and Client. https://www.planttext.com or https://www.websequencediagrams.com are great online tools for creating these diagrams which have really helped me understand why things were happening and when. Also reading the source code to the UNET HLAPI - while a bummer that this is necessary, it will help you immeasurably with groking what is going on. And rereading the whole Networking section of the Unity docs. Simplifying a test case/example project is also good - what is the simplest thing i can get working, with 1 syncvar, so i know that i actually understand what is happening, or at least have working code to build on. These are all the things i do when i get stuck on some bit of networking. Good Luck!

Ok thanks I ll definetly look at that when I have a chance, oh colour was experimental so I kinda rushed it. Thanks!