Network Smoothing - Antonio Roldan



As promised I have updates on how things are going with replication. If you read my last post you will know that after converting all of my blueprints to native C++, I was having issues with animations replicating. With the help of my team I was able to resolve this issue and now my animations are fully replicating from the server to the client. The main thing I was doing wrong here was first of all I thought that I needed to only have these animation instances on the server, and then replicate that to the clients with a multicast. That was not the case however as it's essentially the other way around. While we do need to update variables that drive the animations on the server and have them replicate, we want to have actual instances be on the individual clients and we want to use Multicast functions to send updates to them from the server only. Once I figured that out my animations began replicating as intended but this left me with an entirely new problem.

Even though my animations were now replicated, on the client side they were noticeably choppy.  I wasn't sure what was causing this as this hadn't been a problem before when I was doing the same thing in blueprints. After going through the process of analyzing any differences between my previous work in blueprints and my new work in C++, I found the answer. As I mentioned before I previously was using the Character Movement Component for my enemies which I found out was suboptimal for large amounts of AI, which lead me to switch over to using the Floating Pawn Movement component when I converted to C++. This is where my new problem was stemming from, because the Character Movement Component has built-in network smoothing while the Floating Pawn Movement component does not. So I looked at what they were doing with network smoothing in the Character Movement Component class and saw that it was quite involved, but at the very core it was just simple interpolation. I did some research to see how other people were handling this as well and came up with a solution. 



The crux of the issue comes from the latency between the server and clients and how frequently they are updated. In summary the animations aren't updated fast enough which is why they appear choppy because they are skipping frames while trying to keep in sync with the server. The fix  for this is to do two things. First we needed to keep track of the clients current transform and the desired replicated transform that is being sent in from the server. Then we simply need to interpolate between those two values and set our actor's new transform to be the newly interpolated value. 

To solve the first step we make a transform variable that is replicated from the server using a RepNotify. This is the desired transform of all of the clients as it is what the server's current position, rotation, and scale are. To make sure this is coming from the server we wrap this in an authority check and place it in our Tick function. Then we do something similar for clients, in that we check to make sure we don't have authority and grab our actor's transform and store that in a separate variable. Now that we have what we need we move onto the interpolation. In that RepNotify where we update the server transform every frame we also reset our factor that we are going to use for interpolation which in this case is acting as our "Smoothing Time". This is how long we want our interpolation to take, which for our purposes we want to be very fast so I set this at 0.1 seconds. This value is reduced by delta time each frame and once it reaches zero we set the new transform of our actor. Before that however we actually have to interpolate between our server and client transforms, which we can accomplish by breaking apart our transforms into translation, rotation and scale. For the translation we want to use linear interpolation as it makes the most sense because our positions follow a linear trajectory. For the rotation however we want to use spherical interpolation which we can accomplish with the quaternions we get from our actor's rotation value. Since I know our scale doesn't change I don't worry about interpolating that value, but if you wanted to that would again be a linear interpolation. 

Once I finished implementing this new logic I noticed that the animations were still choppy leaving me very confused. After reading a few articles of people solving similar issues however I realize that the problem was actually because I had marked my enemy actor's to replicate their movement. This was directly conflicting with my new logic as their translations were being set in two different areas. Once I unchecked that setting the animations became very smooth meaning my logic was working as intended. 

Leave a comment

Log in with itch.io to leave a comment.