View Full Version : Timing and Smooth Animation
Hello all,
I am in need of some advise and ideas toward making my game run silky smooth. So far, what I have isn't bad, but could use some improvement. Let me give you a quick overview.
I am developing a 2D (using 3D) scrolling tile based game. This game has big levels (up to 200x200 tiles x3 layers) and lots of moving/animated objects. The game is time based and everything works great, except at low-mid (30-50) FPS the game appears to be jerky.
My main concern is the timer. I use timeGetTime() to resolve my delta time and there are alot of fluctuations in delta. To help, I average a bunch of the last numbers and use that for my time scaling.
I should mention that I run the game loop at a set frequency and render every frame (I think everyone does this). Running code at 100Hz is jerky (I notice, some don't). Switching to higher frequency like 500Hz gives very smooth results, but there is a small issue with that. On slower machines (like my 1.5GHz laptop with 16meg 3D Radeon) the FPS drops to a crawl, due to a huge load of logic, array looping with alot of if statements, and calculations (all forced at 500 times per second). Obviously there is room for optimization, but still, there is a big load of processing.
So my goals are to have the game running silky smooth on even slower machines without having to resort to very high update frequencies. Either that, or I have to figure out a way to optimize it so much that it doesn't matter how fast the code runs. But that isn't simple at all.
So that and the timer... Would using a hardware timer (PerformaceCounter) give me noticibly better results with lower frequency (like say 100Hz)? Or is the best way to go with my current timer and just update the game very fast? That would require some insane optimization tricks, so I would like to discuss this first before diving right in.
I heard that running code at low frequencies and interpolating between geometry animations is another way to go, but I'm not sure if I want to get into that, specially for a 2D game. (but the game does have alot of calculations happening, like vector rotation)
Thanks in advance,
Dima
soniCron
08-18-2005, 01:17 PM
There is a heated debate about timing methods in this thread (http://forums.indiegamer.com/showthread.php?t=3099). You might find your answer there. :)
Robert Cummings
08-18-2005, 01:24 PM
Run your logic at 100fps minimum and render whenever the logic's done each cycle and don't worry about tweening. Works for popcap.
Run your logic at 100fps minimum and render whenever the logic's done each cycle and don't worry about tweening. Works for popcap.This is what I currently do. But, I found that 100FPS isn't enough for silky motion. There are jerks and studders. PopCap has the same problem, and isn't nearly smooth enough for my action/arcade game. Running at higher frequency solves my problem, and makes everything unbelievably smooth, but taxes my laptop big time.
Martoon
08-18-2005, 01:42 PM
If I understand your post correctly, you're doing asynchronous rendering with a fixed-interval update (draw a frame, do however many 1/100th second updates you need to catch up, draw the next frame, and so on).
Your stuttery effect is due to temporal aliasing, but it appears you understand that (some frames have 1 update, while other frames have 2, etc.).
In my personal experience, I've found that the thing that makes temporal aliasing visually apparent is when the entire screen is moving, like a camera panning in 3D or a screen scrolling in 2D. Doing motion interpolation for all of your entities, etc. adds a lot of complexity, but try interpolating just your screen scrolling. That'll usually get rid of the stuttery look.
I won't get into the whole timer debate, but it shouldn't be difficult for you to try QueryPerformanceCounter() instead of timeGetTime() and see what the difference looks like to you.
Martoon, you do understand me correctly. Im not sure that temporal aliasing was an issue on my desktop, since the game ran at 900+ fps at all times, and still there was slight studder. Updating the game at 500Hz gave unbelievably smooth results on that machine. I mean even ghosting on that LCD was barely noticible, it just ran that smooth. No jerks no studders, pure silk. But my laptop got raped, FPS crawled to 5. I should try using the hardware counter and see if that helps with running the game at around 100Hz.
C_Coder
08-18-2005, 01:53 PM
I don't have a fixed timer so I calculate everything according to time deltas. This seems to be very smooth.
Savant
08-18-2005, 01:59 PM
I don't have a fixed timer so I calculate everything according to time deltas. This seems to be very smooth.
This is what I do as well and it works fine.
I don't have a fixed timer so I calculate everything according to time deltas. This seems to be very smooth.I use deltas as well, because I tweak the update frequency alot, or sometimes just disable it. So all my motion is scaled by a delta, which is either calculated every frame, or is fixed number if I update at a specific rate.
Using the approach of update once, render once gives very choppy results due to big fluctuations of deltas. Updating at a frequency of 100Hz gives better results, but there are still noticible studders. Increasing the update frequency gives better and better results, but isn't too practical for crap machines (tons of calculations).
I am starting to think it's all in my timer. I think if I use a better timer like the PerformanceCounter and at the same time run the game at around 200Hz, it should be silk.
This is what I do as well and it works fine.It works fine, but i'm sure not for everything and on every machine. Using straight deltas to scale motion, I notice the studders if the deltas arent same every frame. Averaging deltas helps, but not to a huge degree.
There is a good plus to using a fixed update frequency and render every frame. Since deltas are locked to a static number, you can implement networking, predictability, and replay features much easier since your game goes through the same exact states at all times. For instance, I record key presses and unpresses with the current game state number, and save those to a file. Then I can be sure for a fact that when I replay the level using the saved input data, it will replay exactly as I played it.
soniCron
08-18-2005, 02:31 PM
It works fine, but i'm sure not for everything and on every machine. Using straight deltas to scale motion, I notice the studders if the deltas arent same every frame. Averaging deltas helps, but not to a huge degree. How many deltas are you averaging? In my latest game, I average the last 100 and I get incredibly smooth animation. And, while fixed rate timing is much easier for network play, playback, etc, you really don't have any control over how varying processor speeds handle the load. Going over a 120Hz refresh rate is a terrible idea and a waste of processing in many cases, IMHO.
The reason your current method dies on your laptop is because you're seriously overtaxing the processor, which may be causing it to clock-step, in addition to just being too weak to handle the load. (That, or you're forcing a render each frame, and your laptop can't draw that fast.)
If you truly want to support slower platforms, you need to either target a more reasonable update speed (<=120Hz) or switch to delta timing. It's that simple.
I would love to stay at 100Hz update interval because it does work quiet well, but after seeing the game run at 500Hz it's hard to go back.
I do prefer the fixed step updates, for level replays and such, it just works very well. But it's true that running code at high rates isn't practical, and I understand that. But at the same time I want it to run super silky smooth.
I average 1/2 second of deltas, not a fixed number. I tried averaging 100, 30, and so on, but got some unwanted results on low fps machine. The game would speed up gradually or slow down. And averaging too few of deltas just doesn't keep them stable enough.
I would have thought that 100Hz update interval would be more than enough for smooth animation, but that wasn't the case. Maybe it's my timer, but after seeing the game run silky at 500Hz, I just want it to stay that smooth.
Pyabo
08-18-2005, 03:25 PM
Why do you need to average deltas? Aren't you actually looking for the exact time since the last frame in order to calculate the proper positions for moving objects? What does the averaging get you?
Martoon
08-18-2005, 06:32 PM
Martoon, you do understand me correctly. Im not sure that temporal aliasing was an issue on my desktop, since the game ran at 900+ fps at all times, and still there was slight studder. Updating the game at 500Hz gave unbelievably smooth results on that machine. I mean even ghosting on that LCD was barely noticible, it just ran that smooth. No jerks no studders, pure silk. But my laptop got raped, FPS crawled to 5. I should try using the hardware counter and see if that helps with running the game at around 100Hz.
Even if you're running at 900+ fps, temporal aliasing will still be an issue because of monitor refresh rate. Unless, of course, your monitor is refreshing at 900Hz. ;)
Why do you need to average deltas? Aren't you actually looking for the exact time since the last frame in order to calculate the proper positions for moving objects? What does the averaging get you?The problem with exact time since last frame is that it never stays the same. When you use delta time for you motion and the delta keeps jumping around, your motion gets jerky because objects move different amounts every frame. Say something accesses your hard drive in the background, that would produce an increase in your delta time, but since delta time is how long it took to process last frame, your next frame will scale bigger.
Averaging out a bunch of last deltas and using that average to scale motion will give much smoother results because the delta you use doesn't jump around but stays at the same number or fluctuates very slowly and by small increments.
Even if you're running at 900+ fps, temporal aliasing will still be an issue because of monitor refresh rate. Unless, of course, your monitor is refreshing at 900Hz. ;)hehe, the monitor refreshes at 75Hz. But the game renders at 900+ frames per second. Code runs at 500, and everything is extremely smooth in terms of object motion. I do notice tearing though, but turning on vsync gives completely smooth result.
I'm beginning to think that it's not practical to run my game update at such high rates. That plus having vsync on must completely waste cycles. But getting into tweening doesn't appeal to me that much, I just want to move on to finishing the game.
Might be the best idea to just run update at twice the speed of refresh rate and render every frame with vsync. That way the game has some precision and enough updates to alias the timing and keep things smooth. I think monitors go up to 120Hz, so if I make my game logic run at around 240-250Hz (and tax some CPU in the proccess) and always have vsync on, there shouldn't be problems on the average PC, and high end computers will still be very smooth. Maybe replace the timer, but mine seems to be 1ms accurate.
Jay_Kyburz
08-18-2005, 10:05 PM
The problem with exact time since last frame is that it never stays the same. When you use delta time for you motion and the delta keeps jumping around, your motion gets jerky because objects move different amounts every frame. Say something accesses your hard drive in the background, that would produce an increase in your delta time, but since delta time is how long it took to process last frame, your next frame will scale bigger.
umm im not a programmer, but i would have thaught you would want it to scale bigger to catch up for the frames / time missed.
if object moves 1 unit every ms
10ms frame
move 10 units left
8ms frame
move 8 units left
12ms frame
move 12 units left
10ms frame
move 10 units left.
Please correct me if im wrong, im having all kinds of issues with this in my own game.
Re: PopCap framework
I get very smooth movement by setting:
mVSyncUpdates = true;
during initialization and using:
Widget::UpdateF( float theFrac )
instead of the normal:
Widget::Update()
Update() always gets called at ~100 times per second, while UpdateF() gets called based upon the current screen refresh rate.
I just scale movements using 'theFrac', which is the amount of time (in 100th of a second) since the last call to 'UpdateF()'
So instead of:
new_x = cur_x + step_size_per_tick;
I do:
new_x = cur_x + (step_size_per_tick * theFrac);
One of the demos that comes with the framework shows and explains the hows and whys.
HTH
The main reason I use fixed update rate is for replay saving. What you describe above is using delta time and running with vsync, which is fine, but harder to do replays with. Also, your delta has to be pretty stable and not jump around at all to produce good results.
PeterM
08-18-2005, 11:45 PM
I get pretty smooth movement in the IGE shoot-em-up demo, using GetTickCount(), tweening (of everything) and a basic non-averaging timer.
The source code is in the API docs (http://saurusgames.com/developers/ige/docs/) (in the Examples section).
The IGE averaging timer adds even smoother movement, but I need to add some extra events to IGE to make it worthwhile (since you should reset the timer on display mode changes and such).
Pete
Ska Software
08-19-2005, 06:58 AM
Doom3 caps at 60fps, which, I understand, is around the area where the human eye can't detect more "smoothness" i.e. 60fps and 200fps should look about the same to the human eye.
Oh, and I use getTickCount too. And I hate the word tweening. I like lerping better.
PeterM
08-19-2005, 08:42 AM
Doom3 caps at 60fps, which, I understand, is around the area where the human eye can't detect more "smoothness" i.e. 60fps and 200fps should look about the same to the human eye.You're right there. One of us may have misunderstood though, the non-smoothness I was on about is because the update rate isn't the same as the render rate, so things "jitter" if errors aren't compensated for.
Oh, and I use getTickCount too. And I hate the word tweening. I like lerping better.Me too, I only used tween because it was already used and I didn't want to complicate the issue. If it makes you feel better, I tween using a function called lerp! But then I guess people could tween using more complicated functions, they just wouldn't gain much unless their update rate was very low.
Pete
Ok guys, can you please give more information on how to interpolate? I am interested in knowing if I can set my update code running at 30fps or even lower, then render every frame, but still get silky smooth results? Do I have to predict stuff or is there a straight forward method in doing this? Would be nice to run the logic at such low frequency and still get very smooth results.
Also, getTickCount() and timeGetTime(), what's the main difference? It seems that my timeGetTime() timer is 1ms accurate.
PeterM
08-19-2005, 09:16 AM
I think Gaffer's articles (http://www.gaffer.org/articles/) are the best reference I've found for fixed timestep game loops.
(Plug) IGE has helper classes for aiding in the implementation of a fixed timestep loop.
I'm not entirely sure about the differences between the time functions. I've always used GetTickCount only because it works fine for me.
Pete
Thanks for the article links, they are interesting. I looked over the fixed step stuff, and starting to understand the interpolation method, very interesting indeed. I think for a tile based game such as mine, it might be possible to run game logic at 15fps and interpolate the motion to produce very smooth results. If this works out for me, I will be very happy. I think it's worth the time to try this technique. Definitely sounds better than overtaxing CPU at 500Hz.
PeterM
08-19-2005, 09:48 AM
I'd be careful about choosing an update rate as low as 15 Hz. If you have a character shooting a machine gun (or some other rapidly repeating occurrence), you're going to be stuck with shooting at 15, 7.5, 3.75, 1.875, etc bullets per second. This may not give you the rate you want without having to shoot twice on the same update. You could try a rounder larger figure like 50 or 100 Hz.
Chances are that your rendering is the bottleneck in your game, so upping the update rate will make negligable difference to the frame rate.
Totaly understand your concerns. Once I figure out and set up everything to use interpolation, I can always tweak the frequency of my updates. Quick thinking though tells me that even 15Hz might be enough for my game. I don't have machine guns or anything that is high rate like that. Everything is tile based, and you move by tiles at a time, except that in between tile shifts the motion is smooth. Ok 15Hz might be very low so I might try something like 30, 60, 100Hz, as long as interpolation does what it should, things should work out just fine. (i hope :))
soniCron
08-19-2005, 11:45 AM
Totally understand your concerns. Once I figure out and set up everything to use interpolation, I can always tweak the frequency of my updates. Quick thinking though tells me that even 15Hz might be enough for my game. I don't have machine guns or anything that is high rate like that. Keep in mind you'll have an input latency that, in a worst case scenario, is equal to that of your logic rate. That is, when you're running your logic at 15Hz, you're only able to receive and process keyboard/mouse input every 1000ms/15Hz = 66.66ms, which can be noticeable for keyboard input and annoying as hell for mouse input.
I do agree that input precision might be a problem at such low frequencies. If that will be the case, I'll up the update rate, or think about separating the input into it's own rate. In either case, this should work. I just wrote a small test that just moves the sprite around the screen at 2fps, then interpolates it's position. Very smooth for 2 fps :). Interpolation might be the way to go here, sure beats running updates at super high frequencies. I wonder though what happens if updates run slightly faster than the actualy FPS. In that case interpolation is practically ignored and it seems that there might be a problem with temporal aliasing. At high framerates though things should be very nice.
Has anyone here used this method? I would imagine some small 3D games would do this. Definitely more interesting than just using plain deltas, as replays now are a piece of cake.
Jay_Kyburz
08-19-2005, 04:34 PM
I recently implemented Glenn's fixed step physics in my little game. I didn't do the little extra step of interpolating the renderer between game ticks because it added quite a bit of complexity for not much advantage.
I wanted the fixed step solution for the case where the renderer is very slow. I'm making a strategy game where the frame rate is not vital and I plan throw allot of polygons at it. If my renderer is running faster than 100 fps I should add more polygons rather than interpolate to make the game smooth at 200 fps.
The problem with the fixed step solution is that now I'm locked in to doing all my "game" processing and physics in under 10ms per tick. I'm not a very efficient programmer and this makes me nervous.
I should also add that my first pass at this only updated everything at 20ms (or 50fps) and I found this to be quite stuttered and generally unpleasant to look at. This case needed interpolation. Moving things is about as complex as my game gets so rather than interpolate I just tick everything twice as fast. 10ms or 100fps is smooth enough for me. If I can't do all my game logic, message processing, ai ,collision and network stuff etc in 10ms I may have to increase this number.
PeterM
08-20-2005, 12:08 AM
I think interpolation is really the way to go. However, if you find that your update is taking longer than 10ms, try to spread the work over several frames. For example, instead of doing all your pathfinding every frame, work out a part of a path each frame.
I can't think of anything other than physics and AI that would take so long. As an experiment, why not try commenting out your rendering calls to see how much time your update really takes?
And as ever, you could use some kind of profiling to see where your time is going. IGE has a built-in profiler, which can be used separate from the rest of the library.
PeterM
08-20-2005, 12:16 AM
I wonder though what happens if updates run slightly faster than the actualy FPS. In that case interpolation is practically ignored and it seems that there might be a problem with temporal aliasing. At high framerates though things should be very nice.
Has anyone here used this method? I would imagine some small 3D games would do this. Definitely more interesting than just using plain deltas, as replays now are a piece of cake.I've not seen what happens when updates run slightly faster than renders, but I've seen the case where updates happen a lot faster. This is when using the D3D software driver in the IGE demos. They seem to "play smoothly" even though the graphics are slow. It's quite weird to look at, but rather satisfying to know that it's still playable.
To try it:
Rename your OpenGL driver (c:/windows/system32/nvoglnt.dll, atioglxx.dll or similar) to another name (like adding a leading underscore so you can find the file again).
Go into your DirectX control panel, and disable hardware acceleration for DirectDraw (since this is D3D7).
Run the shoot-em-up example.
Restore hardware acceleration.
Restore your OpenGL driver.
Pete
Ok, so I got it to work last night and quiet happy with the results. I incorporated interpolation for my objects positions and angles, that's pretty much all that needs to be tweened. Then I set the update loop to run at 25 fps, and the results are very smooth. It was definitely worth the trouble to set it up and beats running updates at vey high frequencies.
So all I do is tween the positions and angles. Even the camera and light didn't need that because I just stuck the code in the render function. Same for sprite animations, the code that keeps srpites changing frames I just put beside the rendering loop because It's already time based and doesn't affect gameplay in any ways (this way my sprites animate smoothly on time without having to be confined to the low frequency update loop). Things are looking good now. I do recommend for anyone who needs a fixed step update loop to look into interpolation. Even at low FPS game still plays without jerks and all on time.
PaulCunningham
08-21-2005, 03:58 PM
There is a very detailed thread over at FlipCode for exactly this kind of thing.
http://www.flipcode.org/cgi-bin/fcarticles.cgi?show=63823
vBulletin v3.6.0, Copyright ©2000-2008, Jelsoft Enterprises Ltd.