Razor Code
Rambling about code since quite recently

Topics

User Functions





    Don't have an account yet? Sign up as a New User
    Lost your password?

Events

There are no upcoming events

Older Stories

Sunday 14-Sep

  • ACM ICPC 2008 (0)

  • Monday 11-Aug

  • NZ Programming Comp (0)

  • Sunday 27-Jul

  • Blast from the past (0)

  • Monday 21-Jul

  • Timetable generator again (0)

  • Tuesday 08-Jul

  • Hosting (2)

  • Saturday 05-Apr

  • Sparse Volume (0)

  • Friday 28-Mar

  • Carmack (0)

  • Tuesday 04-Mar

  • Back to Uni (0)
  • Dell Kill Switch Direct (0)

  • Monday 18-Feb

  • Lappy (0)

  •  Time stepping    
     Author: 
     Dated:  Saturday, January 27 2007 @ 03:29 AM NZDT
     Viewed:  238 times  
    ProgrammingIt's been a long time since I've talked about anything technological. I've probably lost that audience entirely, in fact I'm not sure I have an audience at all any more, but I'm writing this regardless. I've mentioned how I'm learning a lot, and I'm going to talk about something I've 80% learned and 20% come up with. It's about time steps, and how to get your game looking as smooth as possible for the least processing cost. And it's very long.

    Beginning game developers inevitably run into a problem. Their games run different speeds on different computers, because they just update as fast as possible. In fact I never ran into that problem, but never mind that. There are a couple of (sensible) ways to overcome this. One is to limit the frame rate of the game, so it can never draw more than, say, 60 frames per second. Not only will this not satisfy the hardware freaks, if intensive calculations or slow hardware cause it to fail to complete the 60 frames, the game will slow down. Matrix style. For networked games this is especially bad, because (if badly done) they could get out of sync. Another option, and my preferred method for quite a while (even when my engine was threaded) was to update as fast as possible, but to base the calculations on a high precision timer. This ensures that the game keeps up with real life, but this has problems of it's own, especially when tackling physics and collision detection. A common example is objects passing through each other or acting strangely on slow computers. The jump between frames could mean an object goes from one side of a wall to the other, without ever actually intersecting it. There are ways of overcoming it, but that's beyond the scope of this post.

    So how do we cope with it? Well it's nice for the physics to have a fixed time step. But it's nice to be able to skip a few frames, and even add more in to smooth it out (if for no other reason than because people have different refresh rate settings). So obviously we need to decouple gameplay/physics and rendering. I've always done this to a degree, and even had them running in separate threads at one point, but for some reason I never actually fixed the physics time step. So, with or without threading (preferably with, to avoid processing spikes when people drag windows around etc.) we make the update time step fixed and leave the graphics free to fill in the gaps. Problem solved, right?

    Sort of. It could be better. Say you picked 50fps for your update frequency, because it's an even number of milliseconds, and it's fast enough to look smooth. And you didn't get thrown off by the windows sleep function, which is almost reliably 10ms late. But the user has a refresh rate of 80Hz, and a graphics card that can keep up. In this case, what their card is actually doing is rendering update 1, update 2, update 2 again, update 3, update 4, update 4 again, and so on (going in and out of phase, but never mind that). I haven't tested extensively, but I'd guess this might be noticeable. I can tell when something drops to 30fps briefly because it can't keep up with vsync, after all. And what's the point in rendering the same data multiple times?

    The common solution I've seen to this problem is interpolation. Keep two sets of location data for all your objects, and use that good old high resolution timer to smoothly interpolate between them when rendering. This works well. You're sometimes rendering frames an entire update old, but given human reaction times that's not significant. But what people don't seem to realise, is that what they're actually doing is integration. You can use the same basic method for normal physics calculations. By storing the previous location and the current one, you have implicitly stored the velocity. But most physics systems store only one location, and explicitly store the velocity, which tends to come out more elegant.

    So what's the reason not to use that same data to smooth out our rendering? There isn't one. It's simple, you just find the difference in time between the render and the update, multiply by the velocity and add to the location. And it's cheap, even slightly cheaper than conventional interpolation I think. Although that method will actually extrapolate ahead of the current data. Personally, I've offset it by half an update, so it's sort of centred about the update, half guessing at the past, half predicting the future. As an added bonus, it's never more than half a frame away from the latest data. UPDATE: Actually, I think how you should do it depends on how you integrate normally. If you're doing good old euler, and you calculate position based on the velocity for the current frame, your render time should always be behind your update time. If you calculate position based on the velocity from the previous frame, you should always extrapolate ahead. If you use RK4, I'd guess you should do what I did.

    A 'problem' with this system is that it will predict wrong, and things will theoretically jump slightly upon receiving a new update. It's not a real problem for 2 reasons: 1. Velocities mostly change fairly smoothly, and in those cases the jump becomes insignificant. 2. The jump was insignificant anyway, because we're interpolating between updates here. It's (usually) easily smaller than the jump from one frame to the next, and we should be updating fast enough to keep things smooth. And if it ever became a problem, hypothetically speaking, (slow motion?) all you would have to do is upgrade the quality of your integration. Store acceleration as well as velocity, and get your objects moving in a series of little curves!

    Ok, so maybe we should leave physics to the physics update, but it's not like we're doing collision detection or solving constraints here. This stuff is cheap, and possibly most importantly, it lets us cut down on real updates (assuming the simulation is stable enough to handle it) and free up processing time. Thereby making time for more frames, or better frames, or more complex physics. And that's always a good thing, isn't it?



    Trackback

    Trackback URL for this entry: http://razorcode.net/trackback.php/TimeStepping

    No trackback comments for this entry.
    Time stepping | 4 comments | Create New Account
    The following comments are owned by whomever posted them. This site is not responsible for what they say.
    Time stepping
    Authored by: Mayhem on Saturday, January 27 2007 @ 02:02 PM NZDT
    I'm not sure if you have seen this, but here is an article that I found interesting
    and probably will find useful once I get to writing my game loop.

    http://dewitters.koonsolo.com/gameloop.html
    Time stepping
    Authored by: Razor on Saturday, January 27 2007 @ 04:41 PM NZDT
    Hadn't seen that before. It's eerily similar to what I wrote! About the only difference in the final approach, is that I offset my rendering by half an update to increase the accuracy of the approximations. Oh, and I actually did it threaded, with an update function that carefully sleeps for the right length of time. Under normal conditions it's never more than 5ms late, and often runs during the correct millisecond.
    Time stepping
    Authored by: Anonymous on Sunday, January 28 2007 @ 03:52 AM NZDT
    It sounds like your approach is probably better. I'll keep it in mind when I get
    to that stage. As I will also have vm execution and pseudo threads to worry
    about it may be more complicated.

    Which reminds me, I still don't have a nice way for C++ to call the script code
    without having the script execute in the same thread all at once. I can't see
    another way other than requiring C++ code to provide callbacks when the
    script is finished or do some funky asm hacking which may not even be
    feasible.

    I'll probably just have to use the callback method.

    Mmmm. I guess I could use a mutex and just wait around until it's done in the
    vm thread. Why didn't I think of that before?

    Anyway, great article - it seems everything is going multiple cpu these days
    so writing games to take advantage of threads is great.
    Time stepping
    Authored by: Razor on Saturday, February 03 2007 @ 04:00 AM NZDT
    Hmm. I was going to say that if you used a mutex you wouldn't gain anything, because nothing would be happening concurrently. But I think I'd missed the actual problem. You have your scripts running in a separate thread to whatever else, but you want to be able to call script functions without ending up with multiple threads executing scripts right? I can see how having arbitrary script functions called from a separate thread would cause major pain. So yeah, it sounds like a mutex is the only reasonable option. If the calling code doesn't need any result, would it be possible to add the call to a queue which would be executed by the vm thread at it's convenience? I'm not sure how useful that would be though.