The following blog post, unless otherwise noted, was written by a member of Gamasutras community.
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.
January this year our FPS game Verdun was suffering from a lot of network traffic. This started causing random disconnects on some systems. On the right you can see an image of our network traffic history. It displays the (average) messages per second in our game rooms. By implementing simple network culling we were able to reduce traffic by 50%; from 2000 messages per second to +/- 1000. The slow increase you see -after- implementing the culling solution is because of an increase in players per room. In the end this helped us to support 32 instead of just 24 players per game.
Finding creative solutions while working with limitations is one of the things I love most while working on games. To no surprise this networking culling solution required just such an creative solution. Networking culling is dead-easy when done server side, but this was not possible. I am using Photon Unity Networking(PUN) for all my multiplayer Unity games (I founded PUN in collaboration with ExitGames). Therefore I chose to work with two limitations:
- Client-side culling(!): We cannot use server logic in the Photon Cloud. The only relevant feature the Photon Server currently offers us is the use of 250 ‘interest groups’. When sending a message you can assign it a group number. Every player can tell the server which groups it is interested in. The server will make sure to only send a group message to those who have subscribed to that group.
- Usability was of big importance to me. The system should be generic and easy to implement in any game since I wanted to be able to share it with other PUN users.
The solution I came up with is a bit geared towards FPS games, but it has been made quite generic so that it can be reused in other games as well. In our game position and rotation updates of players make up over 90% of the network messages. Every player sends 10 – 20 updates per second per default. However, when an enemy player is far away we don’t care about quick updates, since inaccuracy is harder to notice. We only need near real-time updates for players that are very close by. Since only ever care about the latest position updates so culling a few messages won’t hurt the game. The following GIF explains the solution I’ve implemented that automatically takes care of the culling.
PUN Network Culling in Verdun
The system automatically calculates which blue and red areas it’s interested in and subscribes to events of these groups on the server. You will receive 100% of the information for red areas, 50% of the messages from blue areas, and 10% of the messages from players anywhere else. Note that the solution completely ignores the Y axis.
While this looks simple at first, stuff got complicated because of the PUN (client-side) limitations. A player updates it’s position via a “PhotonView” which is assigned a (usually static) group number. However, we needed to send player data over both blue and red areas which could not be mixed and we have only a small amount of groups. In the end I have implemented a system where the PhotonViews group number is changed in between every network update. Ten consequent network message would be send on the following groups: All, Red, Blue, Red, All, Red, Blue, Red, All, Red. As you can see this ensures that you would receive 100% updates for players in a subscribed red area (5 Red + 2 Blue + 3 All = 10/10). If you are subscribed to a certain red area, you are automatically subscribed to that blue area as well (not per se the other way around). All of this is taken care of automatically.
The full source code and more documentation to this solution is now included as free extra in my Photon Unity Networking guide available on the Unity asset store. If you have a multiplayer game using PUN you could use this resource to get allow more simultaneous players in your game rooms. In a PUN game it’s just a matter of adding 1 script to your player prefabs and hooking it up via a single line of code in your player synchronization code.
I hope this can be useful to your games, let me know how much traffic it saved you!