Introduction
In this article series, we will look into implementing entity interpolation using the features provided by the Unity High Level API, or HLAPI. We will continue from where we left off in another tutorial series, and work gradually in six parts:
In Part 1, we will talk about entity interpolation in general, then we will take our single existing script and break it into smaller scripts. This would hopefully prevent our code from turning into an unreadable and unmaintainable mess, allowing us to add in entity interpolation more easily afterwards.
In Part 2, we will change the way input is sent by the client to the server. At the moment, the client can send input information to the server during every frame, which can cause a lot of network traffic. To fix this, we will change our code so that input is batched then sent to the server less frequently.
In Part 3, we will add some safeguards on the server side to prevent a client from cheating by sending too many inputs.
In Part 4, we will change the way we handle the data that we receive from the server. Instead of synchronizing immediately, we put the data received in a buffer, and then introduce a short delay before synchronization. This is in preparation for...
...Part 5, where we will take the buffered state data and interpolate between them, resulting in smooth movement despite the low frequency updates.
Finally, in Part 6, we will tweak our code to better handle an interpolation edge case. We will also look into using interpolation for something other than position data.
Although six parts might seem to be a bit too much, doing it this way should allow us to break up the task into smaller, more manageable pieces, hopefully aiding in your understanding of the concepts involved and helping you see how you can use the techniques demonstrated in your own games.
Prerequisites
This article uses scripts from the first two parts of another Unity 5 Networking Tutorial as the base, so it is assumed that you are familiar with the basics of developing with the Unity 5 Networking High-Level API (HLAPI), and implementing client-side prediction and server reconciliation with the HLAPI.
Our implementation is based on a couple of sources:
Just like before, we reference Gabriel Gambetta's excellent Fast-Paced Multiplayer article series, in particular the third part on entity interpolation. As of the time of writing, the Fast-Paced Multiplayer live demo does not include sample code for entity interpolation, so hopefully developers who are looking to implement entity interpolation in their own projects could borrow from the scripts included in the sample project for this article series.
Our particular design (and probably almost all others) is heavily influenced by the entity interpolation section of the Source Multiplayer Networking article.
It is highly recommended that you read through both of these resources to get a better overall understanding of entity interpolation and its importance in implementing networked multiplayer games.
Download The Project
If you have already downloaded the project folder for the other tutorial series, then you should have received the new project folder for this series as an update. Otherwise, you can download the project folder here.
The What And Why Of Entity Interpolation
The HLAPI makes it very easy to manage distributed state in a networked multiplayer Unity game. Given a variable that we want to synchronize across all connected clients whenever its value is changed, all we have to do is mark it with a SyncVar attribute:
[SyncVar] CubeState state;
"Whenever its value gets changed" needs some additional explanation. The frequency at which SyncVars are actually synchronized is governed by the network send interval. At its default value of 1/10th of a second, this means that a SyncVar is only able to synchronize updates 10 times a second at most.
This can be a problem for variables that change all the time, for instance every time FixedUpdate is called. Consider an object that moves evenly from position 0 to position 50 over 1 second via FixedUpdate. On the server, we will see a smooth progression of values 0, 1, 2, and so on until 50. However, the network send interval would cap SyncVar updates to other clients at 10 times a second. This means that, even in the ideal case, we will initially be at position 0, then a delay, then a jump to position 10, then another delay, then position 20, and so on, leading to choppy movement.
While it is possible to set the send interval to 0 to remove the synchronization frequency limit, doing so can cause too much network traffic, leading to undesirable game performance. We also need to remember that network connections are unreliable, and it is possible for network packets to be dropped occasionally, resulting in missing SyncVar data, as if frames were being skipped. To make smooth multiplayer experiences possible despite the intrinsic limitations of networking, we can use entity interpolation.
In entity interpolation, we embrace the fact that SyncVar updates are present for only a few frames (at most only 10 out of 50 frames in a second) and absent for most others (the remaining 40 or more frames). So to create smooth movement for the clients, we try to fill in the gaps for frames with no SyncVar data using interpolation.
Continuing with our example, suppose we need the position of our entity at time 0.22 seconds, but we do not have the needed SyncVar data. However, we have the SyncVar data at time 0.2 seconds (say, at position 10) and time 0.3 seconds (position 15). Given this information, we can perform interpolation (perhaps linearly) and decide to render our entity at that time at position 11. If we interpolate whenever actual data is absent (instead of doing nothing and making it look like the game has a low frame rate), we are able to hide the effect of low frequency updates.
Note the main limitation with using interpolation: given a missing data point in time that we want to interpolate, we will always need at least two actual data points, one before and another after the needed time. This implies that there will always be a time lag for interpolated entities. For our example, we will use a fixed delay of twice the network send interval (1/5th of a second based on the default value), which would hopefully provide better resiliency against dropped packets.
Six parts? Seriously? I do not have the time right now!
The source code for all six phases is already available for download, and you do not need to wait for the next articles to be published. All you have to do is grab the project folder so that you can try out a working entity interpolation implementation for Unity on your own machine right now.
By stepping through the implementation over six phases, hopefully it should be easier for one to understand how all the pieces fall into place, referring back to the articles as needed to fill in the gaps if some parts of the code are not clear enough.
Our Starting Point
To keep things simple, we put all the code for our previous Unity Networking Tutorial in a single script, and it did all of the following:
As a local player, we read the input device, then...
As a client, we receive updates from the server, then...
make sure that all player objects show the correct state.
As a