PDA

View Full Version : How to achieve smooth movement



czar
09-07-2007, 08:14 PM
My pet annoyance for a long time has been smooth movement. Why is it so difficult to move say a large game sprite from one side of the screen to the other without jittering?

I am hoping people have some suggestions to help improve the way in which sprites can be moved.

Typically the way I do it is as follows.

The coordinates of a 2d or 3d sprite are x,y,z as a single or double.

current location is updated using the speed lag of the system. i.e. the number of milliseconds it takes for the computer to come back and run through a timer loop.

So; x :=x + (100 * Timerlag);

Where timerlag is a proportion for example 16 ms divided by 1000. The 16 ms is the time it takes for the computer to run through the timer again. Using this system I should find that my object travels 100 pixels in one second, and on the whole looks ok. However I find that the movement is never consistent and that the object tends to jerk every once in a while. This is not so noticeable in a game where a sprite constantly switches directions, but it is noticeable when you have a large object that needs to "slide" from one place to another.

With 3d objects I can leave the x,y,z coords as singles but for 2d stuff the sprite is usually drawn by trunc(x). In both cases I still see jerky movements.

I have tried a few optimizations; like finding an average for timerlag over N number of frames and then use that value instead of a constantly changing value. This work to some extent. It has the added benefit that it is not possible for the sprite location to all of a sudden change by a large amount. I have had this occur in the past, Windows would all of a sudden decide to put time into accessing an idle drive and the resulting timerlag would send a sprite straight through a wall or similar.

How do others move their sprites and has any one got an idea to improve the smoothness or consistency of movement?

LP
09-07-2007, 08:26 PM
With 3d objects I can leave the x,y,z coords as singles but for 2d stuff the sprite is usually drawn by trunc(x). In both cases I still see jerky movements.
Assuming that you use Direct3D, why do you truncate coordinates? Just use floats and image will "swim" smoothly. This may generate extra blur, but transitions will be very smooth, especially if using multisampling.

czar
09-07-2007, 08:41 PM
I only truncate for functions that require the coords in integer rather than doubles. e.g. for older delphix stuff. However where possible I do leave it as floats.

dmantione
09-07-2007, 09:05 PM
Vsync is very important for smooth movement. Enable vsync, and use the frame number as the basis of the positions where you draw (if you use the system time there will be a small timing jitter as the frame will only be drawn=shown at the next vsync).

czar
09-07-2007, 09:33 PM
Can you expand upon what you mean by frame number?

Are you saying you move a fixed amount per frame? If so then speed will be dependent on frame rate, faster frame rate, faster game. That would not be good for computers that cannot keep a steady 60 FPS or whatever you set it at.

21o6
09-07-2007, 09:41 PM
what you are seeking is called "timebased movement".

timebased movement is achieved by measuring the time your last frame
took to render multiplied by the units you want to move per second.

maybe you've got it right anyway, i just don't understand what you mean
by "time it takes for the computer to run through the timer again" so i
thought i'll post just to be sure.

see http://www.devmaster.net/wiki/Timing

hope it helped :D

czar
09-07-2007, 10:17 PM
That is exactly what I am doing. It is all timebased rather than frame based.

21o6
09-07-2007, 10:27 PM
could you explain what you mean by "time it takes for the computer to run through the timer again" ?

Does this mean you'r calculating the amount of time the last frame took to complete?

if so, then i really can't think of a reason why animation is jerky... never
had this kind of problem (and i always use timebased movement...).

sorry for not being able to help :)

czar
09-07-2007, 10:44 PM
Most drawings system have it. It is the lag between rendering.

I think you misunderstand when I say it is jerky I am not talking about gross movement I am talking about small jitters that occur sometimes - not noticeable to the average joe.

If I have time I will put a simple example together and post it.

By the sounds of I do my movement the same way as everyone else so I think I will need to learn to live with it.

LP
10-07-2007, 12:03 AM
Try to run the application in full-screen and disable vsync. This will give higher rendering frame rate (unless you are limiting it, which is probably the source of jitter) and increase the precision of frame-based movement.

Also try to optimize your rendering pipeline, since the source of jitter is probably when either your application or Direct3D falls behind the rendering, so Present() takes significantly more time to render than normally.

czar
10-07-2007, 02:16 AM
OK cheers. I will play around with those ideas and see if I can improve it. Many people have trouble seeing what I mean when I show them so it is not too bad :)

Andreaz
10-07-2007, 04:59 AM
Where timerlag is a proportion for example 16 ms divided by 1000. The 16 ms is the time it takes for the computer to run through the timer again. Using this system I should find that my object travels 100 pixels in one second, and on the whole looks ok. However I find that the movement is never consistent and that the object tends to jerk every once in a while. This is not so noticeable in a game where a sprite constantly switches directions, but it is noticeable when you have a large object that needs to "slide" from one place to another.

This part sounds confusing to me, I have never had any problems with unsmooth movement using the timers i've created for Phoenix for instance. What kind of timer do you use and how do you calculate the elapsed time ?

Some example code on how i'm doing it:


var Frequency : Int64;
var CurrentTime: Single:
var LastTime : Single:
var FrameTime : Single:

procedure Initialize;
begin
QueryPerformanceFrequency(Frequency);
end;

procedure Update;
var Time : Int64;
begin
QueryPerformanceCounter(Time);

CurrentTime:= (Time / Frequency);

FrameTime:= CurrentTime - LastTime

LastTime:= CurrentTime;
end;

procedure MoveSprite;
begin
Sprite.X:= Sprite.X + Sprite.Velocity.X * FrameTime;
end;

(note that this is limited to Pentium 3's and above)

If the framerate varies alot you could average the frame time over a few frames aswell to get even smoother movement.

czar
10-07-2007, 05:29 AM
I have been playing around with different variables and the ideas mentioned above and I have figured out that the best movement in my case comes from:

Vsync set, No limited on Max FPS.

OK, excellent guys. I think I will stick with that. I have kept CPU usage to a minimum (1-2% on my machine) in order to prevent the fan coming on in people's laptops.

I am testing this with DanJetX however my concern was not limited to DanjetX because I have also seen it with Omega and DelphiX.

wodzu
10-07-2007, 07:33 AM
Yeah I've also have a problem with those small jitters which seems to be randomly. As far as I know a lot of people have problem with it. It could be even observed on the DelphiX collision demo. The one in which you move a ball to collide with other balls.

Dan
10-07-2007, 09:48 AM
czar,
in the latest version of DanJetX (0.75) the timer has a Process procedure
where you can simply stick all your movement without worrying about
frame dependency (no need to multiply by any speed factor or calculate lags).

dmantione
10-07-2007, 12:58 PM
Can you expand upon what you mean by frame number

Let's say the display is configured for 100Hz. Frame 1 is displayed at t=0,01 and frame 2 is displayed at t=0,02.

Now, say you are moving a sprite on the screen, frame 1 has just been drawn and you are drawing frame 2. If you use the system time, you will get a value between 0,01 and 0,02. If you use this value to calculate the position of the sprite, this will be sligthly off, and slightly unpredictable. The right value to use is 0,02 and not the value somewhere between 0,01 and 0,02 because t=0,02 when the frame will be actually shown.

Firlefanz
10-07-2007, 01:13 PM
@Dan: Right now I am doing it exatly like Czar does. If I want to use the new timer.process, what do I have to change in the movement?
Just move it let's say 4 to the right instead of move it 4*time_elapsed to the right?

Can this also be used for randoms? When I want my sprite to do something from time to time I use random(round(1000*time_elapsed))=0...Interesting thread this is :)

Firle

Mirage
10-07-2007, 04:42 PM
These jerks occurs when a videodriver performs a CPU-GPU syncronization.
You can force the syncronization every frame by reading one pixel from backbuffer or draw something via GDI.
Also sleep(10-30) after D3DDeivce.Present() may help.
All these methods have some FPS cost.

czar
10-07-2007, 07:14 PM
@Mirage - I haven't heard of this before - have you any idea where I could find more info on this? I will google it when I get to work.

Mirage
10-07-2007, 09:27 PM
@Mirage - I haven't heard of this before - have you any idea where I could find more info on this? I will google it when I get to work.

Video card vendors usally publishing related information on their sites.
http://developer.nvidia.com/page/home.html
Also forums on sites like gamedev.net are good source of information.

czar
10-07-2007, 09:46 PM
I specifically meant more info about "videodriver performs a CPU-GPU syncronization."

Mirage
11-07-2007, 08:11 AM
Internal workflow of video drivers is not a thing that vendors explain in details. So if you find official info about this please let me know.;)
I can remember only one good link:
http://tomsdxfaq.blogspot.com/

czar
11-07-2007, 08:27 AM
Cheers - it certainly makes more sense.