Performsinterpolation and extrapolation in order to make your objects smooth and more accurate over the network. Highly configurable, only send what you need. Optionally compress floats to further reduce bandwidth. Customizable interpolation and extrapolation settings depending on your game's needs. Comes with a fully functional example scene. The full source code is provided so you can see everything with detailed comments!
Unlike the NetworkTransform script provided by Unity, the SmoothSync script stores a list of network States to interpolate between. This allows for a more accurate and smoother representation of synced objects.
SmoothSync.teleport() - Used to teleport your objects for situations like respawning so that they don't interpolate.
SmoothSync.forceStateSendNextFixedUpdate() - Useful if you have a low send rate but you want to manually send a transform in between the send rate.
SmoothSync.clearBuffer() - You will need to manually call this on all of the object's instances if you change the object's network owner.
Alternatively, check Smooth Authority Changes to use handle authority changes automatically and smoothly.
When using Mirror you must call AssignAuthorityCallback() on the server after changing authority.
SmoothSync.validateStateMethod - You may set up a validation method to check incoming States to see if cheating may be happening.
Don't hesitate to contact us with any problems, questions, or comments.
With Love,
Noble Whale Studios
I'm trying to eliminate the lag or at least smooth the movements so the lag is not visible but I'm having difficulty. I've played around with the settings on the PhotonView and Photon Transform View as well as the SmoothSyncMovement script below that I attached to my player object but I've had no luck with reducing the lag.
I'm using the Photon Cloud server and not an on-premise local server.
The player game objects that I'm trying to sync across the network are also using Rigidbody.Addforce for player movement. Could this be a factor causing the lag?
without any modification this is 'normal' behaviour and caused by the delay the message needs to 'travel' from one client across the server to another client. If you want to you can take a look at the discussion here which is about the same topic.
In short: the sender can send an additional information which can be used by the receiver in order to try to predict the movement of the object. This way you won't get rid of lag either, but you can lower its occurrence. A documentation page about this topic will be available in the next days / weeks, there is currently no exact ETA about this.
First, create a new Unity project and then import in the Mirror and Smooth Sync assets. Then create a plane to use as a floor and drag in the SteamVR [CameraRig] prefab. Add a primitive object as a child to the camera and controller objects so that we can see them. Last, create a camera that has an overhead view of the whole scene to be used by our server.
Next, we want to add a NetworkIdentity script to our [CameraRig]. Now we add a Smooth Sync Mirror script to the prefab and set the Variables To Sync settings so it only syncs position and Velocity to save on package size. (as a general rule of thumb most of the time you want to turn off scale syncing as you very rarely change it) Now we want to add three more of the same script, set the Child object to sync variables to the Camera and controllers. This will take care of their position and rotations.
This makes it so that when we build our game it has support for a non-VR mode. The last step is to create a .bat file that opens the built game in this non-VR mode. First, build your game to a Builds folder In the root folder of your project, then in the root folder of your project create a file named StartBuild.bat. (A batch file is just a file that contains commands for a command prompt that can be run instead of typing them in to save time.) Now open the file in a text editor and type this in:
Now how I use this is by opening a Non-VR instance to use as a server and then I use the unity preview as a client, but you could also go as far to make the non-VR mode playable so that you can test multiple clients by yourself.
And that this completes this tutorial, I hope I was able to help with some of the early obstacles that most people experience when trying to make a VR multiplayer experience and also that I helped you.
It is assumed in this paper that the client clock is directly synchronized to the server clock modulo the latency of the connection. In other words, the server sends the client, in each update, the value of the server's clock and the client adopts that value as its clock. Thus, the server and client clocks will always be matched, with the client running the same timing somewhat in the past (the amount in the past is equal to the client's current latency). Smoothing out discrepancies in the client clock can be solved in various ways.
Now obviously due to jitter, my interpolation looks terrible as I will receive time + latency jitter. So my question is, is on this part: "Smoothing out discrepancies in the client clock can be solved in various ways."
In my implementation simulation on both client and server is running in discrete "ticks", so I can always slow down client, so it is few ticks behind the currently arriving data according to jitter average, so unless there is "lag spike", the simulation is smooth.
When the client receives state 100, for example, it starts interpolating from the state it already has (state 99) and state 100 such that it should reach state 100 at the same time that it receives state 101 from the server. The server sends state updates at a fixed rate, so this is predictable, but it does inherently introduce a small bit of extra latency (based on the update rate).
If you want to cover up the occasional lost packet, have the client show two states behind the server. For example, when it receives state 100 it starts interpolating from state 98 to 99, such that it should be showing state 99 when state 101 is received. If state 101 doesn't come through (due to a lost packet), it continues interpolating between 99 and 100, anticipating 102 to come through next. When state 102 comes through, it can interpolate between states 100 and 102 over two update cycles, and the player should be none the wiser. This, of course, doubles that extra latency, but with a high enough update rate, is not a problem.
Where you have network jitter, my suggestion is that when updates come later than expected (or two in a row do, if you use the two-cycle smoothing described above), they set the new expectation for latency. When packets come sooner than expected, instead of jumping ahead in time, store those states in a buffer and continue catching up at a consistent rate, assuming that packets may still come through with as much latency as your worst case thus far. Of course, you wouldn't want an unlucky spike in latency to add artificial lag to the rest of the session, so as packets continue to come in sooner than expected, slowly bring that simulation lag down to catch up with the server, perhaps with a buffer if the incoming states vary quite a bit in timing.
As an example, you might expect an update rate of 20 updates a second. That's an update every 50ms. If update 100 comes through slower than expected and you aren't using an extra update cycle of smoothing, you're forced to wait for it. But once it does come through, you interpolate between state 99 and 100 such that you should reach state 100 in 50ms. In that time, since you have all that network jitter, state 101 and 102 arrive. But instead of jumping ahead to them, you store them, consistently taking 50ms to move from one state to another. As you continue to sit a few states behind what you've received, you decide to play catch up and start moving from one state to another in only 49ms (or even less), subtly bringing the player forward in time to enjoy the improved latency their connection has been having without any abrupt changes in simulation speed.
In anticipation of further inconsistencies, you may want to remember the worst delay you've had in the last x number of seconds and stop catching up when you reach that delay again (that is, return to 50ms between each state of you're expecting 20 updates per second).
With a two cycle delay, you can ignore one-off delays, and need only remember the best of two consecutive delayed states in the last x seconds. It could be worth switching between a single cycle delay and two cycle delay depending on the quality of the connection.
The jitter started when I introduced a fixed time loop of heavy operations. In my case I introduced a networking library that runs 60 times per second and therefore added some lag-spikes in the interval. But I have also added some artificial delays (sleep) to unitys-fixed update and notices the same behavior. My game still runs in the expected time-frame of 90 fps, but some frames take about 11 ms and others about 5 ms (example) which makes for a big variation in delta-time.
Variation in frame-times has not been a problem in traditional pc games I have worked with before. The delta time is either made almost constant by the use of v-sync. Or necessary to variate to compensate for the variation in time between each presentation of the frame. And movement results are smooth.
However it seems to be handled a bit differently on the Quest platform, I believe v-sync is not enabled, and instead Oculus use something called "OculusRuntime.WaitToBeginFrame" to keep a fixed frame-rate. The key difference is that "WaitToBeginFrame" is called in the beginning of the frame instead of how v-sync is called after the frame to do the actual waiting. I guess it is made this way to create as little input latency as possible before each rendered frame.
I also assume by looking at my profiler data, that the time to wait in "WaitToBeginFrame" is calculated based on the length of the previous frame. This would work great as long as frames are equally long but with variations in frame-time (as I have when introducing a decoupled network loop) this introduce even more variation by making a frame that already takes more time then the previous wait for the time saved by the previous.
3a8082e126