When users open multiple browser tabs, only the active (visible) one will be updated regularly at a high rate. Inactive (non visible) tabs get only updated once a second in most browsers. Visibility in this case doesn’t mean, that the window has to be in the foreground, it can also be non-focused and hidden by another window.
This also applies to WebGL exports of Unity and may lead to problems as received and outgoing messages are processed only once a second. This means that the message queues are growing and more and more updates need to get processed on an already low update rate. Many apps get more and more out of sync and run into disconnects while their Tab is in the background.
Modifying the WebGL build
Before talking about any of the approaches we want to talk about how we determine if the application tab is in background.
Therefore we hook into the
visibilitychange event and listen for changes.
We are differing in two states:
hidden and "any other state".
Whenever we detect a state change we can send a message to a game object from our game logic and execute a function that way.
In the above case we are sending a message to a game object called 'Receiver'.
On this object we are executing a function called
This function can be placed inside any script attached to the game object.
To attach this code to your WebGL application, navigate to your build and open the created index.html file.
You can copy and paste the above snippet and insert it into line 34 inside the
<body> tag of that html file.
Don't forget to adjust the parameters of the 'SendMessage' function.
First Approach - Closing The Connection
The first approach will disconnect the client from Photon when the WebGL application tab goes in background. It will try to (re-)-connect to Photon again when the application gets in foreground again. This is the recommended way to handle the described scenario.
To do so we are using the code snippet from above in our index.html file and sending messages to a 'Disconnect' and a 'Reconnect 'function from our game logic. These function might look like the following examples:
Note: The boolean variable
wasConnected is set to true when the client had joined a room before.
Having this information we can try to directly reconnect and rejoin the room instead of joining the lobby first.
PlayerTtl(describes how long a player can be inactive after a disconnect before getting removed from the server; in milliseconds) to a value larger than 0. If you want to keep an empty room you can additionally set
EmptyRoomTtl(describes how long an empty room is kept in the server's memory before getting removed; in milliseconds) to a value larger than 0, too. When testing
ReconnectAndRejoinusing this approach it might be a good idea to set both values to 30,000 (30 seconds) or even 60,000 (60 seconds).
Second Approach - Using Interest Groups And Keeping The Connection Alive
The second approach requires you doing additional work on your game logic and especially your networking logic. Using this approach we are trying to keep the connection alive without sending or receiving game related messages. This is achieved by using Interest Groups in order to quickly subscribe to or unsubscribe from a certain group.
As written above we need to use another Interest Group when using this approach. For the following example we use group 1 as we can freely subscribe to and unsubscribe from it.
When connecting to Photon make sure that you enable sending and receiving for your desired group. You can do this by calling:
Also make sure that your instantiated game objects (those which have a PhotonView component attached to them) have set their group variable to your desired group as well, for example by using:
Third Approach - Forcing Updates And Keeping The Connection Alive
This one creates and destroys a timer based on the current visibility state.
When this timer is active it calls the given function each 250 ms or four times a second.
The interval can be adjusted to your needs as well as the parameters of the
Having this updated we can continue adjusting our WebGL project as well.
Therefore we create a function
DoSomething which will be called from the previous introduced timer.
This function might look like the following:
This increases the network's message flow even if the browser's tab is in background. Although this sounds like a good idea this approach has a big disadvantage, too. Due to the fact that you can't control your character when the game is actually not visible you don't change anything related to your character's transform as well. So you will always send the exact same information to other clients. Means that you have an increased count of messages with no important content. So all in all this approach is not recommended, too.
Fourth Approach - Adjusting The Third Approach To Keep The Connection Alive
Even if the third approach is not recommended we can use it for our last approach. Therefore we modify our 'DoSomething' function so that it looks like the following:
This one simply send acknowledgments to the server in order to keep the connection alive. No game logic related messages are processed in this additionally created intervals.