Sync Object state with all other clients.

Hi, i have a problem with my game i was working on this problem like 3 hours without any succes…
So i have a box model which have an animator with Open and close animations. I want that animations to sync with all other clients. The animations switch betwen Open and Close when player press ‘F’ via this script

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

public class Interactions : NetworkBehaviour {
    [SerializeField] private Camera playerCamera;
    [SerializeField] private LayerMask layerMask;
    bool isOpen2;

    void Update()
    {
        if (Input.GetKeyDown (KeyCode.F))
        {
            Interact ();
        }
    }

    void Interact()
    {
            RaycastHit hit;
            Ray ray = new Ray (transform.position, transform.forward);
            if(Physics.Raycast(ray,out hit,3f,layerMask))
            {
            isOpen2 = hit.collider.GetComponent<Box> ().isOpen;
                if (hit.collider.CompareTag ("Openable"))
                {
                hit.collider.GetComponent<Box> ().isOpen = !isOpen2;
                }
            }
        }

}

And this is the box code

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

public class Box : NetworkBehaviour {
    [SyncVar]public bool isOpen = false;

    void Update()
    {
        if (isOpen == false)
        {
            this.GetComponent<Animator>().SetBool("isOpen",false);
        }
        else
        {
            this.GetComponent<Animator>().SetBool("isOpen",true);
        }
    }
}

When i play the game the box opens only on player side which interact with the object… I don’t have any clue how to do that i already made 2h of research and i didn’t find anything which could help me.

Syncvars, such as your bool isOpen, only update to clients when the value is set on the server. If you want a client to update this value you would do so via a Command. You call the Command on the client, which actually runs the code on the server. In the Command function you change the syncvar value, which then propagates to all clients.

A Command can only be run on an object the client either has authority over, or is that client’s Player GameObject. So generally what you would do is have the Command on the Player GameObject, and then on the server side the Command updates that SyncVar.

So do you do this with ClientRPC? Or do you use server-side prediction? If we use server-side prediction, how do we do so?

P.S. I am facing the same problem. I am making a football game and cannot sync football to the clients.

I think your football You can sync via NetworkTransform component. BTW. i will try to modify my code.

I actually tried doing that but it ended up being really choppy on the client side. Also the client could not control the ball since it had no authority. Now how do I give it authority when the ball enters it’s trigger?

Well im really new to Unet and i don’t really understand it. You mean i should do the interact() function with [Command] or i should do a function e.x.

[Command]
Cmd_SynchValueVar(bool _isOpen){
   isOpen = _isOpen;
}

I don’t have any ideas how to do that…

Sorry i can’t answer your question because im new to Unet :wink:

So in your first script you are trying to modify a SyncVar in Interact, but that won’t work the way you want if this is run on a client. You could create a Command on a script on the Player gameobject that you call instead of setting that SyncVar locally (still call Interact locally, but just call the command to adjust the SyncVar value). The Command on the Player gameobject just sets the SyncVar on the server side. I’m not familiar with raycasts and animations to say if everything else will work fine, but I know you’d at least have the SyncVar value set correctly.

//Should i do something like this?

Interact(){
RaycastHit hit;
            Ray ray = new Ray (transform.position, transform.forward);
            if(Physics.Raycast(ray,out hit,3f,layerMask))
            {
            isOpen2 = hit.collider.GetComponent<Box> ().isOpen;
                if (hit.collider.CompareTag ("Openable"))
                {
                hit.collider.GetComponent<Box> ().isOpen = !isOpen2;
               CmdSyncState();
                }
            }
}

[Command]
void CmdSyncState(bool _isOpen){
isOpen = _isOpen;
}

Well i’ve been experimenting with my code and it still don’t work… My code updated:

Interactions.cs:

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

public class Interactions : NetworkBehaviour {
    [SerializeField] private Camera playerCamera;
    [SerializeField] private LayerMask layerMask;

    void Update()
    {
        if (Input.GetKeyDown (KeyCode.F))
        {
            bool isOpen_ = new bool();
            Interact (isOpen_);
        }
    }

    void Interact(bool isOpen)
    {
        isOpen = false;
            RaycastHit hit;
            Ray ray = new Ray (transform.position, transform.forward);
            if(Physics.Raycast(ray,out hit,3f,layerMask))
            {
                if (hit.collider.CompareTag ("Openable"))
                {
                isOpen = hit.collider.GetComponent<Box> ().isOpen;
                isOpen = !isOpen;
                CmdSyncState (hit.collider.GetComponent<Box> ().isOpen, isOpen);
                Debug.Log (hit.collider.GetComponent<Box> ().isOpen);
                Debug.Log (isOpen);
            }
            }
        }
    [Command]
    void CmdSyncState(bool obj, bool obj2){
        obj = obj2;
    }

}

Box.cs:

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

public class Box : NetworkBehaviour {
    [SyncVar]public bool isOpen = false;

    void Update()
    {
        if (isOpen == false)
        {
            this.GetComponent<Animator>().SetBool("isOpen",false);
        }
        else
        {
            this.GetComponent<Animator>().SetBool("isOpen",true);
        }
    }
}

I don’t really know what to do… It’s freaking me off right now.

You’re doing your command wrong. You’re sending the bool values as seen on the client to the server, then assigning one to the other, but not actually setting anything in the Box class. You need the Command to get its own reference to the Box and set the value on the box.

A little refresher on c# “value types” such as bool.

Actually i made a little progress :slight_smile: But that progress generated another problems xD When i change state of isOpen true/false on the host, it updates on every client, but when i change state of isOpen true/false on client, it doesn’t update on host… Unet makes me really stupid.

Updated code:

Interactions.cs

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

public class Interactions : NetworkBehaviour {
    [SerializeField] private Camera playerCamera;
    [SerializeField] private LayerMask layerMask;
    [SerializeField] private GameObject lastObject;

    void Update()
    {
        if (Input.GetKeyDown (KeyCode.F))
        {
            Interact ();
        }
    }

    void Interact()
    {
           
            RaycastHit hit;
            Ray ray = new Ray (transform.position, transform.forward);
            if(Physics.Raycast(ray,out hit,3f,layerMask))
            {
                if (hit.collider.CompareTag ("Openable"))
                {
                lastObject = hit.collider.gameObject;
                Box box = hit.collider.GetComponent<Box> ();
                box.isOpen = !box.isOpen;
                CmdSyncState (box.isOpen);
                Debug.Log ("Local state: " + box.isOpen);
            }
            }
        }
    [Command]
    void CmdSyncState(bool isOpen){
        lastObject.GetComponent<Box> ().isOpen = isOpen;
    }

}

Box.cs is still the same.