• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
sufficientreason

Smoothing Corrections to Client-Side Prediction?

9 posts in this topic

I'm rebooting an old project on a multi-player top-down 2D space shooter using an authoritative server.
 
I have client-side prediction working (based on various articles like Gabriel Gambetta and gafferongames) as follows:
 
Every tick, the client...
1) Receives and applies snapshots from the server (potentially skipping old ones)
2) Collects input
3) Applies input locally and simulates
4) Logs that input as well as the ship's resulting position with a client timestamp C
5) Sends input to the server with timestamp C

Every tick (same rate as client), the server...
1) Receives and input from clients (potentially skipping some if the client is moving too fast)
2) Applies input and updates the world
3) Sends out snapshots to clients (including C)

At the client's step (1), when it gets snapshot S, that snapshot contains C. So the client rolls back through its logs to find the input that it sent marked C (trashing anything older), and finds the corresponding local position P for when that input was sent to the server. If S and P are far enough away from each other, it's time to correct. The naive way to do this, which works fine, is to set P to S (i.e. replace the position I had stored in the past with the position I just got from the server, also in the past), and re-simulate forward through all of my stored input, applying that input to the new updated position and updating the corresponding states in my input log with the updates ones. That works fine, here's how it looks with some ping and packet loss:

ForkedYawningIberianchiffchaff.gif

Blue is the predicted player position. Red is the received server position (S). Green is the stored position that we're rolling back to when we get the server position and correcting (P). The white dots represent all of the positions between green and blue, and those dots turn red when we did a correction this tick.

You can see jumps from the packet loss, but overall it's alright.

My problem comes from wanting to smooth those jumps. I got the bright idea that, rather than just setting P to S directly, I could do something like P += (S - P) * 0.3 -- ideally this would cause some softer adjustments. However, what this seems to cause is these sorts of cyclical adjustments in rapid succession, even when there's no input or anything that would cause the client to deviate from the server:
 
BrilliantMeatyAmericanbulldog.gif
 
All I changed in my code was making it a 30% snap instead of a 100% snap. The idea being that I'd rather slowly nudge the correction towards the right position than snap it outright. Is there something conceptually I'm missing here? Is there any other way to smooth these snaps that I could do instead? When I'm not hitting any keys at the end of the gif and the dots are still solid red, shouldn't the green ship slowly be getting dragged back towards the red? It seems to stay right in the middle of the blue and red ship, which is strange to me.
 
I have a hunch that the fact that my ships have a lot of momentum (as opposed to FPS games where your player stands still if you take your hands off the controls) could cause these repeating corrections, but I'm not sure.
 
Any advice would be greatly appreciated. Thanks! Edited by sufficientreason
2

Share this post


Link to post
Share on other sites
What works pretty well is to do the following:

Let's assume nominal update rate is 100 ms per packet.
Each time you receive a position/velocity packet, calculate where the player will be 100 ms from now, based on that packet.
Then calculate what direction/velocity is needed from where you last rendered the player to make the player be rendered in that position in 100 milliseconds.
Then use that calculation as the position/velocity for the next 100 ms.

The EPIC library (Entity Position Interpolation Code) does this: http://www.mindcontrol.org/~hplus/epic/
0

Share this post


Link to post
Share on other sites
A few questions:

1) How does that fold in with the normal input-based update for the player?
2) My updates are 20ms (give or take), so doing it in one update is still too fast for a smooth transition. If I spread it out over multiple frames, how do I keep from...
- (a) the adjustment getting out of date
- (b) the the client still thinking the player has the wrong position (because it does) and constantly re-correcting? Edited by sufficientreason
0

Share this post


Link to post
Share on other sites

My updates are 20ms (give or take), so doing it in one update is still too fast for a smooth transition


First: 50 Hz network rate? That's pretty aggressive. Most players network ping will be significantly higher than 20 ms, and the packet overhead will be significant at that data rate. Are you sure you need a network rate this fast?

Second: Have you actually tried it? I don't think applying the delta over more than one network frame will be too bad, unless you have a really substantial network glitch, and if you do, nothing you do will feel right to the player. Better get back in "sync" as quickly as possible!

Third: How you update the player simulation based on input is actually entirely orthogonal to how you decide to display the players (both local and remote.)

If you want to apply a delta over time, then you have to model that explicitly. Keep a "delta amount" variable and a "delta time" variable. When you want to apply X amount of delta over time T, increment the delta by X and set the time-remaining to T. If there is another delta already being applied, that will then also be spread over the new interval -- you can "bake in" that by taking the current delta in effect when you calculate the new desired delta.
0

Share this post


Link to post
Share on other sites

First: 50 Hz network rate? That's pretty aggressive. Most players network ping will be significantly higher than 20 ms, and the packet overhead will be significant at that data rate. Are you sure you need a network rate this fast?


Whoops. I knew that looked wrong when I saw it again. I meant 20Hz, not 20ms. I want to eventually do multiple physics ticks per snapshot/input message, say 2:1, but right now everything is just locked to a 20Hz heartbeat for simplicity's sake.
 

Second: Have you actually tried it? I don't think applying the delta over more than one network frame will be too bad, unless you have a really substantial network glitch, and if you do, nothing you do will feel right to the player. Better get back in "sync" as quickly as possible!


Right now, when I'm drawing a ship each frame (or really, setting the transform of the 3D model), I look at the ship's last in-world position and current in-world position, and just lerp between their X, Y, and angle. I'm going to switch to the v' = v + (dv * dt) approach though because I realize it also gives you extrapolation for free.

Anyway, because I'm doing this smoothing, the answer is yes, I have tried it. Here's what it looks like in practice:

DapperTatteredBalloonfish.gif

That's with 100ms ping and 3% PL each way simulated. It's still too jumpy for my liking, and I'd like to actually smooth it out over more than just one 20Hz tick.

For comparison (though it looks awful), here's what it looks like with 5Hz ticks rather than 20Hz ticks. The smoothing hangs at certain points and I'm not sure why, but I don't think it's noticeable at the faster tick rate. I just did this to prove to myself that it was, in fact, doing smoothing over the correction jumps.

OrangeAdoredCanary.gif
 
What I would really like to do is have different levels of adjustment. I'm fine with big jumps for big corrections, but I'd like a much softer way to do small adjustments and make them less noticeable by spreading them across multiple frames.

My nuclear option is to have a third layer of indirection between the player (blue) and the server (red) ship. Something like a smoothing ship that gets dragged along a little behind the player ship at all points, and every frame it cuts its distance between itself and the player ship by half or something, but that seems slushy and like a cop-out.
0

Share this post


Link to post
Share on other sites
Just an update. I went ahead and switched from state tweening to doing the v' = v + (dv * dt) smoothing technique and it got rid of those hangs (thanks to extrapolation!). Here's some updated gifs, again at 100ms ping and 3% PL each way.

20Hz updates (no debug markers):

EnergeticBonyEidolonhelvum.gif

And 5Hz updates (now with debug markers):

GiftedBleakAfricanfisheagle.gif

I still think the corrections look too jumpy, but maybe I'm being pedantic. I still think a tiered approach would work really well though, but I just don't know how best to spread corrections out over multiple frames.

If you want to apply a delta over time, then you have to model that explicitly. Keep a "delta amount" variable and a "delta time" variable. When you want to apply X amount of delta over time T, increment the delta by X and set the time-remaining to T. If there is another delta already being applied, that will then also be spread over the new interval -- you can "bake in" that by taking the current delta in effect when you calculate the new desired delta.


Could you elaborate a little more here on what you mean by "baking in" the old delta when calculating the new? Is there a formula I could look at? Thanks!
0

Share this post


Link to post
Share on other sites

Is there a formula I could look at?


Calculate the delta as "where you actually are displayed" (Pd) minus "where you would want to be" (Pt) and call it Pc (for correction.)
Set correction duration to Ct and start-of-correction-time St to "now."
Then, set the "where you actually area" position (Pa) to "where you would want to be" (Pt)
So, to receive a correction:

Pc <= Pd(t) - Pt
Ct = DURATION
St = t
Pa <= Pt
Now, as you update Pa with time advancing, you calculate the display position as follows:

if t > St + Ct: Pc <= 0
Pd(t) <= Pa(t) + Pc * (1 - (St + Ct - t) / Ct)
t is "now" in each of these.


Btw: Smoothing makes more sense as your display frame rate is higher than your network rate. Also, simulation rate should typically be higher than your display rate, or at least higher than your network rate.
The lower display is more what I'd expect the correct-over-one-tick-with-extrapolation to look like; the upper one is not what I was proposing you do.
0

Share this post


Link to post
Share on other sites
Right. That makes sense. It's just linearly interpolating between the current position and desired position. What do you do if you get a correction while you're already applying one, though? Do you just throw out the old correction, or is there a way to get some sort of curve-style layering of corrections? How do you layer corrections while still ultimately guaranteeing convergence once the correction layers are finished?

Or am I just overthinking this? My movement is still too choppy at even just 100ms/3%PL each way and I'm not sure what I'm doing wrong. I'm getting a lot of corrections when I don't think I should be. The only thing I'm not doing is rolling back the server for input, but I feel like all that will do is cause snapping on everyone else's screen instead of just the controlling player's.

I wish I knew what Gimbal/Cosmic Rift/Ring Runner/Infantry/Subspace/Continuum do because they all tackle this momentum problem.
 

Btw: Smoothing makes more sense as your display frame rate is higher than your network rate. Also, simulation rate should typically be higher than your display rate, or at least higher than your network rate.
The lower display is more what I'd expect the correct-over-one-tick-with-extrapolation to look like; the upper one is not what I was proposing you do.


Do you mean the two gifs I posted? Currently the game both does one physics update and sends out screenshot at the same step at 20Hz with a 60Hz frame rate. The 5Hz update was just an example to show smoothing in action.

I can raise the simulation rate to 40Hz or 60Hz and send out snapshots every 2-3 sim ticks, but I don't think that's going to fix my problem with desync and corrections. If anything, it'll cause more problems because I'll need to send 2-3 input updates per client->server update, and then I'll definitely have to rollback on the server to retroactively apply old input (this will also be exaggerated error because of inaccuracies in estimating the true client-server time delta). Edited by sufficientreason
0

Share this post


Link to post
Share on other sites

What do you do if you get a correction while you're already applying one, though?


The system I proposed updates the correction to say that you interpolate between what is currently displayed (which includes some part of correction for the last correction) and where you want to go. So, while you're "throwing away" the previous interpolation, you're using the result of that interpolation as a baseline for the new interpolation.

I can raise the simulation rate to 40Hz or 60Hz and send out snapshots every 2-3 sim ticks, but I don't think that's going to fix my problem with desync and corrections.


You will de-sync, because it's the internet, so nothing will fix that :-)

You really have two options: One is to display all remote entities in the past (once you're supposed to receive their commands,) which means they will have natural movements and corrections will be small, or you display all remote entities forward extrapolated to "now," which means that any correction, or just received commands, will generate significant jumping.
0

Share this post


Link to post
Share on other sites

I recognise that this is an older post, but I feel there's something else to add.

 

For local players, the most important thing is input responsiveness. For remote players, something like EPIC would be a means of smoothing irregular updates due to packet loss. However, movement packets which are lost before the server receives them (in time) incur miscorrection on the server, for the next move (assuming that the lost packet contained changed inputs).

 

The only means that I can think to correct this is to send redundant previous moves to the server, (I.e 6 moves worth). If the server looses input for T 2, 3, 4 and receives moves for T5 +, it can use the last 3 moves to recover.

 

This will fix the stuttering corrections on the client, for this scenario.

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0