Why do I have to use an RPC and PhotonSerializedView to change color?

Options
NWDEV
NWDEV
I have some code where I change color based on a raycast calling LookedAt(). What I want to know is why does it work...

I have heard that you should either use an rpc or OnPhotonSerializedView(); however, I had to use both to get my color changes to proliferate across all players. If I only used an RPC to all players the colors didn't sync up. If I only serialized the values, only the masterClient's changes proliferated.

How should each work, and how should this be implemented?

Here is my code:
using UnityEngine;

[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(Material))]
public class ColorChanger : Photon.MonoBehaviour 
{
    private float _r, _g, _b;

    void Update()
    {
        this.gameObject.GetComponent<MeshRenderer>().material.color = new Color(_r, _g, _b);
    }

    public void LookedAt()
    {
        //tell the masterclient to change the color.
        PhotonNetwork.RPC(photonView, "ChangeColor", PhotonTargets.MasterClient, false, null);
    }

    [PunRPC]
    private void ChangeColor()
    {
        _r = Random.Range(0f, 1f);
        _g = Random.Range(0f, 1f);
        _b = Random.Range(0f, 1f);
    }

    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.isWriting)
        {
            stream.SendNext(_r);
            stream.SendNext(_g);
            stream.SendNext(_b);
        }
        else
        {
            _r = (float)stream.ReceiveNext();
            _g = (float)stream.ReceiveNext();
            _b = (float)stream.ReceiveNext();
        }
    }

}

Answers

  • NWDEV
    Options
    Oh, and in case it is needed, this script is attached to a cube. Here is how the cube is instantiated in the OnJoinedRoom function:
    //instantiate a scene object that everyone can see and persists after disconnect.
            if (photonView.isMine && PhotonNetwork.isMasterClient) //make sure you are the master client
            {
                Vector3 redCubePosition = new Vector3(1, 1, 3);
                PhotonNetwork.InstantiateSceneObject(   CubePrefabName,         // name (string)
                                                        redCubePosition,        // position
                                                        Quaternion.identity,    // rotation
                                                        0,                      // group ID?
                                                        new object[] { });      // info about object?
            }
  • vadim
    vadim mod
    edited January 2016
    Options
    You do not need OnPhotonSerializeView if sending RPC to PhotonTargets.All
    OnPhotonSerializeView always sets color of all cube instances to color of owner's instance (probably master in you case).
  • NWDEV
    Options
    I did try commenting out the OnPhotonSerializeView and just calling the RPC on everyone.
    If I comment out the OnPhotonSerializeView and I use PhotonTargets.All it will call the function on every player; however, although the colors change on every player, the colors never match each other.
  • NWDEV
    NWDEV
    edited January 2016
    Options
    In order to make the colors match for all players I RPC'd the changeColor on the masterClient. After changing the color I used another RPC to UpdateColor(object[] rgb) on PhotonTargets.others.

    This always had the master change the color of the scene object, and then update it to all other players.

    OnPhotonSerializeView seems to only proliferate colors from the owner of the object, to everyone else. That means that if a non-owner changes the color for example, he only sees it on his own screen, and it doesn't proliferate to anyone else. If an object owner changes the color using OnPhotonSerializeView then it proliferates to all others; everyone else sees the change.

    This is why when I used both an RPC (to the master) and OnPhotonSerializeView everyone saw everyone else's color changes. Everyone also sees everyone else's changes if I call an RPC to master, change the color, and then call an rpc on everyone else and pass the color I just changed to them.

    Wow, so confusing to learn.

    Here is the new code:
    public void LookedAt()
        {
            //tell the masterclient to change the color.
            PhotonNetwork.RPC(photonView, "ChangeColor", PhotonTargets.MasterClient, false, null);
        }
        [PunRPC]
        private void ChangeColor()
        {
            _r = Random.Range(0f, 1f);
            _g = Random.Range(0f, 1f);
            _b = Random.Range(0f, 1f);
    
            // tell everyone else what the new color is
            object rgb = new object[] { _r, _g, _b };
            PhotonNetwork.RPC(photonView, "UpdateColor", PhotonTargets.Others, false, rgb);
        }
    
        [PunRPC]
        private void UpdateColor(object[] rgb)
        {
            _r = (float)rgb[0];
            _g = (float)rgb[1];
            _b = (float)rgb[2];
        }
  • kittyLLLL
    Options
    It could be that you're only telling the MasterClient (which would be only one person per session) to "ChangeColor" and then having everyone else "UpdateColor" to a color that they aren't familiar with- even if you grab the current color the object is, it will probably still appear to be that same color when you're grabbing it. How about instead of

    PhotonNetwork.RPC(photonView, "ChangeColor", PhotonTargets.MasterClient, false, null);

    You change it to

    PhotonNetwork.RPC(photonView, "ChangeColor", PhotonTargets.Others, false, null);
    Or
    PhotonNetwork.RPC(photonView, "ChangeColor", PhotonTargets.AllBuffered, false, null);

    This way you're calling one less RPC, Less overhead!

    Unless the MasterClient is the one that is supposed to be changing color? Then you just make the original target someone else who calls the RPC.