1 free read left. Create a free account for unlimited reading + AI research.
Create free accountproducer are we live producer producer ryan we're good to go okay hello everyone welcome to saturday school number three is all of you probably know network based games are becoming more and more popular the technology behind it is getting more complex and so we have with us a really fantastic speaker basu dev patel hey guys and uh he's a lead unity engineer here at lila and i'll let you go ahead and explain what you're talking about take it away basidav all right so the titles sorry the stock titled structure of network action games so so the there's kind of these fundamentals of how we should think about games and that's kind of what the stock is going to be about but these fundamentals also help us uh
write multiplayer games in a way where you know they look smooth they are very responsive responsive means they the game responds to user input like almost immediately so and that's very important uh increasingly and besides this there's another quality that we want in games like this and that's determinism so we'll be talking about that so before we get go further like how we actually should think about games is like what is a game really like it's so we have a function okay like let's call it a game like let's call it a simulation so what does the simulation do it takes the current state of the world the player's input and produces a new state and this state is what we use to render the next frame right and you can think of almost everything in a game as some version of this so like let's take player movement as an
example or even weapons really they're all really like just some or the other version of this so you can think of any game really as uh you know this function game making up like i don't know like hundreds of thousands of lines of just these little tiny uh things that take state input you know calculate the next uh state in a way and we run this in a loop right and that's what essentially drives the game forward like frame to frame and you see things moving on the screen so that's what it is really at the heart of it so let's take a closer look at what you know a typical move function might look like right so we have a current position and the direction we want to move in which is uh based on okay what keys do are you pressing right now so w might mean that i want to move forward right or uh d means i want to move right w plus d means i want to move uh forward and right so we take the move sorry the movement direction increment the position by it and we get
a new position right and that's how it kind of goes uh now if you want to move faster or slower sure so let's just you know scale the movement direction and so now by uh increasing the value of speed or lowering the value of speed we're able to move faster or slower right but there's a small problem with this and that's you know on faster computers we're gonna be running uh this loop much more often so what that means is that uh this position increment is going to happen more often which means that on different computers our game will play differently right and i guess like almost everybody really knows what the solution to that is right and this is one of the first things that we all learn as game developers is you scale the same calculation by just the time that's elapsed right and that's kind of it but there's couple of problems with that i'm going to get to it shortly so let's take projectile motion
as an example okay so projectile motion and implementation projectile motion will look something like this right so now only instead of incrementing position we are also incrementing velocity right so velocity changes the same way as position except the rate of change for velocity is now acceleration so we multiply acceleration with delta time and we have a new velocity and we use the same velocity to calculate new position and this is what we call semi-implicit euler uh integration and this is what a lot of physics engines actually use right and this is actually good enough for a lot of cases right and but there's something here that uh all of us need to understand okay so one problem with this is that the output of our calculation kind of depends now on how delta time varies okay so the larger delta time is
right like the faster we'll get to these values but that's kind of okay that's what we want in a way right we want to scale the calculation by time right that's what we did want but this has some negative side effects on how this actually plays out like so this is a plot of projectile motion right so the white perfect white line is what uh the trajectory of uh this particular projectile should be okay but if you do the same calculation at different frame rates these are these are the results we get okay and even worse this white line is what you get if your uh delta time varies which can happen on the same computer okay so there's a pretty simple trick to uh working around this or fixing this and
that's just use fixed time steps okay and that's what really all physics engines do okay and this brings us to networking okay so in [Music] uh like most modern uh networked action games what happens is uh position of players and the state of the world changes very very often okay which is why the state of the world has to also be synchronized very often so most of these modern multiplayer games uh at least the multiplayer engine is written in a way where uh they use a tick based system which is pretty similar to uh the fixed update in a physics engine okay the other nice like side effect of using ticks instead of time is that we are using an integer value which is uh like two computers cannot disagree on uh you know how that should translate to the delta time okay because floating point number when we transferred over
the wire like we might have to compress the bits and the value might mean something or if you actually use a time stamp then that might need to be compressed we can't really we can send the whole four bytes but it might still uh if we end up compressing it then the accuracy is gonna vary so in though in multiplayer games now how you can start thinking about game the same state from you know state and input is think of uh state at tick hundred as the games stayed at tick 99 and the input at 99 and we get the new state right and this is kind of how a multiplayer game drives uh like drives forward and every time what a dedicated server will do is it will take um like all of this changes in state at a particular tick and uh stamp them you know with with what thick they are and send it to all the clients so that everybody is in sync and everybody's rendering the same world at the same tick in fact like like uh
in in the real world the time the actual like wall clock time that that that you see somebody move at might be different okay but the game's internal state is still being driven by the stick so what that means is that like a tick 101 may have occurred at a different gmt time for me than uh somebody else right but and that discrepancy will always be there and there's not a lot we can do about that so this uh way of however driving a game forward make gives us a uniform frame of reference so so far we've kind of established that okay so uh if you want determinism we should use fixed time steps okay so this is two in physics engines and in uh networked action games right but and so we can probably thinking okay sure so like if we we can probably go ahead and start writing all of our uh gameplay in the fixed update right then right
but a couple of problems with that and that is so let's say that if we were uh doing our gameplay calculations 30 times a second that's not going to appear great on displays that even run at 60 hertz which like you know this display is also basically 60 hertz right so it's almost like we are displaying the world at half the refresh rate of this monitor and monitors that support like 120 to 144 hertz are becoming increasingly common so this is uh kind of not a good thing because our customers care about this a lot so one way to fix this is that we decouple our [Music] simulation and our rendering so this is a typical unity structure okay we have fixed update let's say and we have uh our render loop oh wait a second okay i think
uh never mind so what this is actually like a bit more complex example there should be a simpler example here but okay so what's actually going on here is so let's say we have a function right the projectile function it's the same one uh that we saw a couple of slides ago right the same function right so we have the same function now we're driving we're running it in fixed update at let's say 30 hertz okay so our time dot fixed delta time is going to be 1 by 30 right so every 30 times a second we're going to calculate a new position and velocity for this projectile okay all we really really do then is we override the uh render position with this new position okay here we would also overwrite velocity as well which let me see if i actually have an example of that okay i do have an example of that sure so if we overwrite we can now override the position that we
should be rendering at okay and in update if we chose to directly use this value then we would still see the hitching okay that hitching is still not solved so one way to solve for that is that we can smoothly interpolate between uh two uh previous values okay so that's what's really going on here so what we do is we first remember a previous simulated position okay simulate a new position and remember that and then in our update loop in our render loop we're basically slowly learning from this value to this value that's essentially what's going on over here okay so in the case of projectile motion there's a pretty simple way to do this which is really just repeat the simulation in render but instead of now uh remembering just a render position we also remember a velocity and we use these values to simulate a head okay so this is actually not really using historical data okay you can think of this as uh we ran a fixed update some
time ago and now let's say we are some time into rendering after our last fixed update and in that time we've actually been calculating a render position but we actually don't do any game logic here like we will not uh process things like uh did the projectile hit something i mean we could choose to do that like if we wanted to let's say show a decal somewhere that the bullet hit or like the ball bounce whatever right we could choose to show some effect here but there would be no uh like game logic happening here all game logic would be happening here here we'd simply just take position and velocity and kind of render ahead in a way so okay same slide again so let me show you an example of what this will look like when applied to player movement
so we have a capsule here representing the player if i hit the wsid keys and the thing moves around so this is what it looks like if we were simulating the game at 10 hertz like 10 times a second right which is not really that great right so now let's say if we were actually interpolating the position then this is what we'd actually see right but the thing that's actually going on here is if you notice the blue ghost that's actually what my um actual like simulated state is so what's kind of happening is that rendering is catching up to what has been simulated okay so when i actually hit a key there's actually a delay there's actually a delay of around one by tenth of a second before my character starts moving okay which my which some people are actually sensitive to okay so this yet again is like it's okay
but not uh good enough for you know what where we want to get to right cool so i guess um see i think i mixed up the slides here a bit i'm so sorry ok so in the case of uh projectiles at least there's another like a nifty little trick that we can use to render ahead okay um actually before that let me just show you what happens in the case of the player so in the case of players so so far we've been uh rendering has been catching up to what
has been simulated right uh let's say that we instead what we do is we render a head like the same way how we're doing the projectile right now what we're gonna do is we're gonna render ahead in fixed update we're going to store our previous position and then use the same input and then just render ahead right so what is that going to look like second i think this is broke sorry
okay there we go so now we are actually rendering ahead here okay so what you'll notice happens is uh this uh so our play is actually moving ahead of what's been simulated now okay which is why the blue ghost shows up behind so now we're not catching up like instead what's happening is we're predicting where we'll be okay but this introduces this uh error in our calculation right if you notice so the reason this happens is because a fixed update hasn't actually uh picked up on the input that we're using to calculate a render state so what that means is that we've uh and and because it run at different tick
rates like this is what ends up happening which is what if you remember is what we noticed in the projectile motion right that if we uh sample the same simulation at different tick rates and we get different results and that's kind of what's going on here okay and so at 10 hertz this looks pretty bad but uh if you were simulating the world at something more reasonable like 25 or 30 hertz actually wouldn't look so bad and uh this is kind of like a good enough solution to uh deal with the problem and in the case of projectile motion though there's a very nice way for us to completely avoid this problem altogether by simply using the closed form solution for a projectile which is you calculate a new position using ut plus half a t square so this way like our render and our simulation are never going to be out of sync they're always going to be they'll
always have the exact and accurate result so a couple of things that we have to do in order to first deal with the error is we need to also change the way we handle input okay so what happens is so let's go back here so let's say so this is a fixed update right which is running at 30 hertz so in order to do player movement we'd have to use position here to calculate uh sorry player input here to calculate new position right but then what what what input value are we going to use do we use an input value that's arriving at 30 times a second though that's not the case right the thing is in in render we probably will be using an input that's arriving at 60 or even 100 times a second so that in itself is going to cause like this uh discrepancy or there's going to be this error so in a way we kind of have to accumulate uh input that we receive and uh update which is you know the 100
times a second to be used in the one uh that we're running at 30 hertz so you can you can kind of picture this as uh let's say if we were rendering at 60 hertz then we'd uh you know get input twice do rendering and then we'll run a fixed update okay do rendering twice then do a fixed update right so we kind of need to um gather up all the movement input here okay now you can probably just store that in a buffer okay but a much more simpler way to do that is to just store whatever movement direction is multiplied by the time and keep also accumulating the time okay and when it's time to actually use fixed update uh sorry the input and fixed update we simply divide the accumulated movement direction by our accumulated time this is kind of close to what you would actually end up doing in the calculation so this is a neat way of you know instead
this is a neat replacement for uh you know using a having having to use an array of inputs right which might be as bad as like up to four or eight in if you were rendering way more often than if you were simulating right so there's kind of a way that we can deal with that uh discrepancy in uh what we're rendering and uh what we're simulating so what we'll have to do is we have to perform some error correction because this error is bound to creep in at least in the case of player input right sorry in case of player movement so one way of dealing with one very simple way of dealing with this is that we uh slowly tend the render position towards uh whatever the real position is like we slowly just loop it towards the actual simulated position right so let me just show you guys what that looks like
so what we're going to do now is so you'll notice that there was this let me show you the previous result just to be sure so you can see that when i change direction there's uh this bit of a snap right it doesn't actually like appear smooth as such okay inputs sorry unity is not really taking my input so so here you can notice that it's been now smoothed out so this is using the same uh thing that's just talking about that we slowly tend slowly move the rendered position towards where we actually should be and what that really looks like is kind of like this so if you remember the same pattern that we were using earlier of uh simulating uh the world storing
uh you know like a base so showing these base values that we have to now uh predict render on top of but instead here what we have is we actually have three sets of values so you can think of like we have position and velocity which is our actual like state of the world okay and then we have a reference render velocity and a reference render position this is the uh these are the positions and velocities that will slowly move towards okay and besides this we actually have a render position and a render velocity which is what we actually use for rendering okay and here we basically perform we just use the same function just different inputs okay which is if you go back to the same thing that we've been talking about where you know games are new state equals old state and input and time so that's just all three of them here we're using different value set of values here and these values are being moved towards these values using these which is just a simple vector3 to move towards and so
that's kind of the gist of it so just to recap bunch of things that we talked about so you got to start thinking of games as simulations okay we need to start breaking up all the individual things that also happen in them as the simulations of state input time equals new state and should be implementing games using fixed if you want uh determinism but that means that your game is not going to be responsive anymore so which is what we do is basically we render ahead but being able but having the ability to render ahead it's really important that you start breaking up your game as in terms of you know new state equals simulate state and input and time and in some cases only we have to correct if absolutely necessary at least in the case of player movement we had to in the case of projectile motion you won't have to and there are quite a lot of like uh physical phenomenon that you might simulate in the game where you wouldn't
have to do this but some of them might require a different uh approach but uh this way of thinking really remains the same it's like okay how do i do the simulation and how do i render ahead and what should my simulation function look like and if you look at most of the examples here they're almost always the same thing like our accelerate move is the simulation function that we're using here it's just that it's using different inputs at different in different parts of the pipeline and that's really the case with almost all of them like its case with projectile as well using the same projectile function here and so yeah that's basically it [Applause]
all right yeah um so in a game like valgrind where you're simulating at 120 hertz would you still end up doing some sort of uh correction because you're simulating at 120 you're rendering at 120 so then would any sort of prediction or anything like that uh if you're if you're running on a display that can render faster than 128 yes then yes you you have so you we have monitors that go to 240 hertz so on monitors like that you would have to do that so it's not just about the monitor really it's also about the computer the computer and the monitor can both display up to 240hz and you have to like we have so [Music] rendering ahead is actually a trick really but but we have to give the player the illusion that they're moving immediately when they hit a key right [Music] so in case of prediction um can you talk a little bit about how does
uh player to player collision or project tie to player collision would happen because you are predicting uh right so when you whatever you are seeing on the screen you expect things to collide in that state um so any high level things like how does collision happen in in a predicted predicted state or yeah huh so we so again like going back to the same example i was showing so if we simulate uh like at rates of like 30 or 60 hertz if our server runs out that kind of tick rate or a simulation also runs that kind of tick rate then uh we can probably do away with do with not having to deal with what you're talking about but i get what you're saying that it's like okay like if i'm if i'm rendering more often then i can probably like i'll probably like run into a wall right but then when the simulation actually runs i'm gonna get pushed out and yes that's going to happen and error correction can probably deal with that to some degree but it will that discrepancy will
become lesser and lesser if your tick rate is higher for lower tick rates we kind of have to deal with it if you don't want to deal with it then the only other solution is that you uh you you do the exact same simulation again in rendering which is so in a character controller part of the simulation would be also performing collision logic so you could deal with that as well you could do it that way as well so similar question um so one is likelier to player collision or player to environment collision the other one is just hit detection uh like when you see somebody on the screen so one is like you you uh when we talk about prediction we are being predicted on our own uh game yeah uh but the other players who we see they are probably not let's say they're not predicted they're just interpolated yeah so if i see somebody i should uh someone from a predicted state to an interpolated state like what are the ways of uh hit detection uh which there are multiple ways but
any way you can talk about like okay i hit somebody who is in interpolated state i'm in a predicted state how would the heat detection work in a dedicated server environment uh so uh again going back to the same example of uh just a second let me find the slide so if you remember we were talking about how we remember state by take right so here also like it's input by take so you've kind of have a buffer main like the server can kind of maintain a buffer of a previous state uh this previous state doesn't have to be for the entire world it can just be for players and uh when we shoot somebody there are a couple of things that matter here right it looks like it's when did we shoot so that's your like uh input at what tick like so let's say in this case 99 right but let's say i shot you at uh 87 right so at 87 so i receive
input for 87 and then input sorry fire input for 87 and then i can roll back the state to 87 and check in a more advanced case you could also do things like uh you know not uh store the state very often right like only store like only uh every third or every fourth state and then interpolate between them that's one thing that's another thing that you can do or even better what you can do is you can keep all of the states and also like interpolate between them like if your input also has some sort of an alpha right like if it's not on the sim but it's between two sims so that that's what we call uh that's lack of pronunciation basically all right okay well everyone that's it we've got snacks on the back please help yourself and thanks for coming everybody thank you
The presentation outlines the fundamental architecture required for building responsive, deterministic networked action games. The core thesis posits that game logic should be treated as a simulation function where the current state and player input, processed over time, produce a new state. To achieve both determinism and responsiveness, developers must decouple the simulation loop from the rendering loop, utilizing fixed time steps for the former to ensure consistency across different hardware while employing interpolation or prediction techniques for the latter to maintain visual smoothness.
Key technical findings emphasize that relying on variable frame rates for physics calculations leads to inconsistent gameplay. By implementing a tick-based system—similar to a fixed update in physics engines—developers can ensure that all clients and the server maintain a synchronized frame of reference. Because floating-point errors can cause discrepancies in network synchronization, the use of integer-based ticks is recommended for state updates. To address the inherent latency between simulation ticks and high-refresh-rate displays, the talk advocates for rendering ahead or interpolating between states, noting that while these techniques introduce potential errors, they can be mitigated through error correction methods like slowly blending the rendered position toward the actual simulated position.
The scope of this analysis covers multiplayer action game development, specifically focusing on Unity-based environments. The methodology relies on theoretical physics integration models, such as semi-implicit Euler integration, and practical implementation strategies for handling player movement and projectile motion. The discussion concludes that while higher tick rates reduce the need for complex prediction, developers must often implement rollback mechanisms or state buffering on the server to handle hit detection and collision accurately, ensuring that actions taken by players in a predicted state are validated against the server’s authoritative history.