Scene Switching and Synchronization Through RPC

Options
So, I've been stuck on this problem for a few months now. I just discovered a solution that I guess I knew was always there, but hesitated to go down that route. Essentially, the logic is: if a remote player is in a different scene, we should turn off a bunch of components (renderer, collider, etc.), so their presence is imperceptible to the local player. That's what this solution does:

[code2=csharp]//Irrelevant code has been omitted
int sceneID=0;
void Update(){
if (!photonView.isMine){ //Remote Player
for(int i=0;i<transform.childCount;i++){ //All of the components we'll turn on/off are on the children of this transform
transform.GetChild(i).gameObject.SetActive(sceneID==Application.loadedLevel); //set the remote player's children to be enabled or disabled depending on their relative scene
}
}
}
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info){
if (stream.isWriting){ //Local Player: Send others our data
stream.SendNext(Application.loadedLevel); //Sending Application.loadedLevel, an integer that identifies the scene (in a 2-scene build, this will be either 0 or 1)
}else{ //Remote Player: Receive data
sceneID = (int)stream.ReceiveNext(); //Set sceneID to be the remote player's scene
}
}[/code2]

The problem with this solution is that it is a little heavier than (I think) it needs to be. Adding a little integer to the stream doesn't seem so bad until you get a lot of avatars in the build. And, regardless of whether it significantly affects performance, why should I have to update something that only changes once in a relatively long while? So my initial idea was to only send an update when a level was loaded. Unity has a Monobehavior method for handling this: OnLevelWasLoaded. So when that was triggered, I'd send an RPC call with the scene integer to everyone. They would appropriately turn on or off the sending player's components and send a RPC call back to the sending player with THEIR scene integer, so the one who started all of this could turn their components on or off. It's a Call and Response structure. Here is the code:

[code2=csharp][RPC]
void SyncScene(int sceneID,PhotonMessageInfo info){
for(int i=0;i<info.photonView.transform.childCount;i++){
info.photonView.transform.GetChild(i).gameObject.SetActive(sceneID==Application.loadedLevel); //set the local player's children to be enabled or disabled in the remote client depending on their relative scene
}
photonView.RPC("ReturnSyncScene",info.sender,Application.loadedLevel); //Send the sender our current scene
}

[RPC]
void ReturnSyncScene(int sceneID,PhotonMessageInfo info){
for(int i=0;i<info.photonView.transform.childCount;i++){
info.photonView.transform.GetChild(i).gameObject.SetActive(sceneID==Application.loadedLevel); //set the remote player's children to be enabled or disabled on the local client depending on their relative scene
}
}

void OnLevelWasLoaded(int level){
if(photonView.isMine){
photonView.RPC("SyncScene",PhotonTargets.Others,level); //Send everyone the local player's new scene
}
}[/code2]

The problem with this was that changing scenes seemed to make the Photon network a little buggy. For a while, the process would run, but the local player would be both the sender and receiver and end up turning its own component's off. Lately, the first RPC isn't even called. When debugging photonView.isMine in the OnLevelWasLoaded method, it returns false, even though it is true (a simple GUI button hooked up to debug the value proves this). I thought that maybe the scene switching was causing a short lapse in connectivity and I just needed to delay the reading of the value. I put the whole thing in a Coroutine triggered by OnLevelWasLoaded with a delay of 5 seconds and it still came up false.

TL;DR: Changing scenes causes Photon Network to display some unpredictable behavior. RPC calls are more efficient than Photon stream for what I'm trying to do, but they seem to be getting mixed up as the localplayer doesn't maintain the photonView.isMine variable through the scene change.

Does anyone have any experience with this or ideas on what may be going on?

Thanks!

Comments

  • If players are in different scenes, they won't receive RPC's calls to each other. So a common thing to do is pause the message queue right before you tell players to switch the level, then when players have loaded to the new scene, unpause the message queue. It seems like that could explain the more unpredictable behavior your having, when different players are and are not receiving RPC calls.

    The coroutine seems like a good way to pause it, but it won't stop the message queue from running if anything is going, and anything sent will be lost calls if they don't make it to their target.

    So the main problem there is that you're trying to send RPC calls between scenes, and to my knowledge that doesn't work. I could be wrong about sending it backwards, but I know you can't send them forward to the new scene.

    Maybe you could do something like pause the message queue, send all players to the second scene, have them have a fake loading screen so no one can see the scene yet, unpause the message queue. Then grab the one player you want everyone's scene to copy, get that player's info, send the RPC to everyone, and make the other players reload to match? Seems kind of like a hack and Tobias or someone may have a better solution, but I hope that all makes sense.
  • By the strictest definition, I guess RPC calling between two scenes does not work. But I'd say that it does, because there is a straightforward way to simulate it. By calling DoNotDestroyOnLoad() on the players' gameobject, I can keep the players from being destroyed when the scene changes, effectively keeping an entity of them at all times in every scene. Since Photon is scene-blind, it doesn't care that two players are in different scenes when looking for the PhotonTarget of the RPC.

    So RPC calling "between scenes" is possible and I've been doing pretty successfully except for the moment at which the scene changes.

    Also, you mentioned pausing the message queue. I've also tried that, unsuccessfully. :( And I'm afraid moving everyone over to one scene and then back every time someone changes the scene is a little impractical. But thank you for taking the time to put forward some suggestions! :)
  • That's good to know, never I thought them switching scenes broke the link, but that could be useful.

    Sorry I wasn't more help, hope you get an answer soon, you could do some cool stuff once that's working.