How to make state of Players different bettween each clients

I have a Player prefab which has a StateManager script controll different states of Player (and current weapon too). These variable are change when use press 1/2/3/4/5, and current weapon of their player change too :

public bool normal, melee, rifle, pistol, grenade;

I want to know how to make these variable different in each client when players press the keys.

Well, if the variable are not static and you don’t need to see the selection of the other players, you don’t need to do actually anything?

You should probably still make sure that the player only impacts the value of the player object assigned to him (so, for each of 5 players, you’d get - locally - 4 player objects with the default values and 1 - still locally - player object - the one assigned to the current player - with the adequate values).

If you need to see what weapons is equipped by the other players, you’d need to use commands to handle the weapon selection and use syncvars to propagate the information to the other clients.

But, basically;

protected void Update() {
if (isLocalPlayer) {
if (Input.GetKeyDown(KeyCode.1)) { // Not sure of the constant there, typing this from memory
melee = true;
// eventually set the other flags to false
}
// handle the other cases the same way
}
}

If you do this, the player object attached to you will get the other values, which the representation of the other player objects will remain untouched.

1 Like

I used syncvar to adjust these bool variable and success. But when I use command (code below), client Players cannnot see current weapon of other Players. And I know I can’t use syncvar with GameObject type. What did I do wrong ?

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

public class WeaponManager : NetworkBehaviour {
    public GameObject currentWeapon;
    public GameObject hand;
    public GameObject meleeWeapon;
    public GameObject rifleWeapon;
    public GameObject pistolWeapon;
    public GameObject grenadeWeapon;
   
    // Use this for initialization
    void Start () {
        CmdSetNormalWeapon ();
    }
   
    // Update is called once per frame
    void Update () {
        if(!isLocalPlayer)
        {
            return;
        }
        if (GetComponent<StateManager>().normal)
        {
            if (currentWeapon != null && currentWeapon != hand)
            {
                CmdDisableCurrentWeapon();
            }
            CmdSetNormalWeapon();
        }
        if (meleeWeapon != null)
        {
            if (GetComponent<StateManager>().melee)
            {
                if (currentWeapon != null && currentWeapon != hand)
                {
                    CmdDisableCurrentWeapon();
                }
                CmdSetMeleeWeapon();
            }
        }
        if (rifleWeapon != null)
        {
            if (GetComponent<StateManager>().rifle)
            {
                if (currentWeapon != null && currentWeapon != hand)
                {
                    CmdDisableCurrentWeapon();
                }
                CmdSetRifleWeapon();
            }
        }
        if (pistolWeapon != null)
        {
            if (GetComponent<StateManager>().pistol)
            {
                if (currentWeapon != null && currentWeapon != hand)
                {
                    CmdDisableCurrentWeapon();
                }

            }
        }
    }

    [Command]
    void CmdSetNormalWeapon()
    {
        currentWeapon = hand;
        currentWeapon.SetActive (true);
    }

    [Command]
    void CmdSetMeleeWeapon()
    {
        currentWeapon = meleeWeapon;
        currentWeapon.SetActive (true);
    }

    [Command]
    void CmdSetRifleWeapon()
    {
        currentWeapon = rifleWeapon;
        currentWeapon.SetActive (true);
    }

    [Command]
    void CmdDisableCurrentWeapon()
    {
        currentWeapon.SetActive (false);
    }
}

[Edit] You asked what you did wrong and I forgot to reply… Actually, you’re only changing the equiped weapon on the server (and the player being host is client and server at the same time, which is why it could have worked for him). But the commands only tell the server about the change, so you also need a communication from the server to the clients. This can be done through SyncVars (but with more primitive values) or Client RPCs. [End of the edit]

You could have a mapping between integer values and the relative GameObject.

The local player would sent the integer value of the selected weapon, and the server would propagate that value through a single SyncVar. At the level of the clients, you’d add a “hook” method on that SyncVar so that when another player equips a weapon, you can get back the gameobject of that weapon and activate it.

Also:

  1. Your code might work on the host (not even sure, but you probably tested), as the current weapon is only changed at the server level (in the command). So a simple client would not have his weapon selected.

  2. In the sample I provide below, I select the weapon directly for the current player, but in the SyncVar hook’s for the other players; that’s only to make the selection in real time for the player doing the action and avoid lag if the network is slow. You might reconsider this based on the logic of your game.

  3. In the sample, I’m using an array of GameObject instead of separate GameObject for the weapons, because it makes it shorter to type. Feel free to keep your version and just have some logic to manually convert prefab to integer. :wink:

A bit of pseudo code (could be correct, typed this in notepad so not even sure it would compile);

public class WeaponManager : NetworkBehaviour {
    public GameObject[] weapons; // Setup through the editor as for the separate fields

    [SyncVar(hook = "SelectWeapon")]
    public int selectedWeaponId;

    public GameObject selectedWeapon;

    void Start() {
        CmdSelectWeapon(0);
    }

    void Update() {
        if (!isLocalPlayer) {
            return;
        } else {
            int newWeaponId = GetComponent<StateManager>().selectedWeapon; // Set to 0 for melee, 1 for rifle (the same as the index in the array defined in the editor)... -1 for the default value
            if (newWeaponId != -1 && newWeapon != selectedWeaponId) {
                CmdSelectWeapon(newWeaponId);

                if (selectedWeapon) {
                    selectedWeapon.setActive(false);
                }
                selectedWeapon = weapons[newWeaponId];
                if (selectedWeapon) {
                    selectedWeapon.setActive(true);
                }
            }
        }
    }

    [Command]
    private void CmdSelectWeapon(int newWeaponId) {
        selectedWeaponId = newWeaponId;
    }

    private void SelectWeapon(int newWeaponId{
        if (!isLocalPlayer) {
            selectedWeaponId = newWeaponId;

            if (selectedWeapon) {
                selectedWeapon.setActive(false);
            }
            selectedWeapon = weapons[newWeaponId];
            if (selectedWeapon) {
                selectedWeapon.setActive(true);
            }
        }
    }
}
1 Like

Clients still cannot see current weapon of others. I checked in the inspector, the syncvar variables worked fine, but currentWeapon (GameObject) didn’t update when selectedWeapon changed.

If you put some debug logs in the hook methods, is it called correctly (at the non-host client level)?
Also, is it my code or you code you copy-pasted (selectedWeapon = …)?

If it’s yours, then it’s weird that you’re assign the value to a variable called “selectedWeapon”, while the one it the editor is called “currentWeapon”…

1 Like

No, I changed selectedWeapon to my currentWeapon. I tried to call a ClientRPC method in Hook method to assign the currentWeapon GameObject, then in host-client everything works fine, but other clients still can’t update this variable.

Well, normally you don’t need to use a ClientRPC.

The hook method is called automatically at the level of the client when a new value is sent by the server.
So the hook method specified for the SyncVar should be called with the new value as parameter, and it should be called for each clients.

Could you run the (non-host) client within the editor, after having put some debugging logs in the hook method, to see if it’s called for that client?