Skip to main content
GameDev.net gamedev.net
🔒 Locked

SDL Time-based movement without using float

Started by rbento Feb 25, 2009 at 1:54 PM 9 replies 7.8k views
Original Post
rbento
rbento
Hello people! I've been reading the forums for like 4 years and I could find answers for my previous doubts without posting threads. But about this time-based stuff I couldn't go through and I need your help. This is it. I am doing a SDL 2D game and I'm using delta time for the movement, and it works fine, but its a little annoyingly choppy. My game is running at 60fps and the delta time i get is around 0.001f and 0.003f. I wonder if the problem is that this delta time is a float, and the object position and speed are integers. SDL_Rect also uses Sint16 for its x & y coordinates, and the result of the movement calc is a float which is cast to an int before being assigned to the SDL_Rect. The only thing I see is that there might be a loss of precision here causing the choppy effect. The direction is either 1 or -1; positionX += (int) (speedX * deltaTime) * directionX; positionY += (int) (speedY * deltaTime) * directionY; Do you guys know a way to do time-based movement without using float in the calc? What do you recommend for a smooth time-based movement with SDL? Thanks in advance.
evillive2
evillive2
I have had luck using floats as long as everything is a float and only pass ints to the rendering methods.

When it comes time to render make sure you round the float instead of just cast it which truncates the float.

float position_x = 235.68f;
float position_y = 500.34f;
float speed_x = some_float_speed;
float speed_y = some_float_speed;

position_x += speed_x*time_delta_float;
position_x += speed_y*time_delta_float;

render_sprite_at( (int)(pos_x+0.5f), (int)(pos_y+0.5f) );

As long as you keep the position and speed info as floats you won't lose "as much" precision and it should help with the stutter.

Another more practical approach may be to google for fixed point math.

Hope that helps :)
Evillive2
rbento
rbento
Hi, thanks for replying!

I did what you've suggested but I've got the same results. It's still a little bit choppy.

My variables are all floats now, and there is no more casts in the calc, only when assigning the position values to the SDL_Rect.

Here is some of what I have:

The sprite.h
...
class Sprite
{
private:

float positionX, positionY;
float speedX, speedY;
float directionX, directionY;

...

public:

virtual void move(float deltaTime);

...
};
...

The sprite.cpp
...
void Sprite::move(float deltaTime)
{
if (deltaTime > 0.0f)
{
positionX += (speedX * deltaTime) * directionX;
positionY += (speedY * deltaTime) * directionY;

rectDest.x = (int) positionX;
rectDest.y = (int) positionY;
}
}
...

And here is some debug information...

It is running with a speed x & y of 20.0f. As we can see, the positions in float are ok, but when the conversion happens it loses too much precision.

:: info :: initializing game...
:: info :: initializing graphics...
:: info :: instantiating game logic...
:: info :: running...
:: debug :: deltaTime: 0.017
:: debug :: pos x & y: 0.34 & 0.34
:: debug :: rectDest x & y: 0 & 0
:: debug :: deltaTime: 0.044
:: debug :: pos x & y: 1.22 & 1.22
:: debug :: rectDest x & y: 1 & 1
:: debug :: deltaTime: 0.027
:: debug :: pos x & y: 1.76 & 1.76
:: debug :: rectDest x & y: 1 & 1
:: debug :: deltaTime: 0.026
:: debug :: pos x & y: 2.28 & 2.28
:: debug :: rectDest x & y: 2 & 2
:: debug :: deltaTime: 0.023
:: debug :: pos x & y: 2.74 & 2.74
:: debug :: rectDest x & y: 2 & 2
:: debug :: deltaTime: 0.026
:: debug :: pos x & y: 3.26 & 3.26
:: debug :: rectDest x & y: 3 & 3
:: debug :: deltaTime: 0.027
:: debug :: pos x & y: 3.8 & 3.8
:: debug :: rectDest x & y: 3 & 3
:: debug :: deltaTime: 0.026
:: debug :: pos x & y: 4.32 & 4.32
:: debug :: rectDest x & y: 4 & 4
:: debug :: deltaTime: 0.027
:: debug :: pos x & y: 4.86 & 4.86
:: debug :: rectDest x & y: 4 & 4
:: debug :: deltaTime: 0.027
:: debug :: pos x & y: 5.4 & 5.4
:: debug :: rectDest x & y: 5 & 5
:: debug :: deltaTime: 0.026
:: debug :: pos x & y: 5.92 & 5.92
:: debug :: rectDest x & y: 5 & 5
:: debug :: deltaTime: 0.027
:: debug :: pos x & y: 6.46 & 6.46
:: debug :: rectDest x & y: 6 & 6
:: debug :: deltaTime: 0.027
:: debug :: pos x & y: 7 & 7
:: debug :: rectDest x & y: 7 & 7
:: debug :: deltaTime: 0.024
:: debug :: pos x & y: 7.48 & 7.48
:: debug :: rectDest x & y: 7 & 7
:: debug :: deltaTime: 0.026
:: debug :: pos x & y: 8 & 8
:: debug :: rectDest x & y: 8 & 8
:: debug :: deltaTime: 0.025
:: debug :: pos x & y: 8.5 & 8.5
:: debug :: rectDest x & y: 8 & 8
:: debug :: deltaTime: 0.025
:: debug :: pos x & y: 9 & 9
:: debug :: rectDest x & y: 9 & 9
:: debug :: deltaTime: 0.029
:: debug :: pos x & y: 9.58 & 9.58
:: debug :: rectDest x & y: 9 & 9
:: debug :: deltaTime: 0.029
:: debug :: pos x & y: 10.16 & 10.16
:: debug :: rectDest x & y: 10 & 10
:: info :: deleting game logic...
:: info :: freeing graphics...
:: info :: done.

I wonder why SDL_Rect has positions stored as Sint16 and not float. :(
If we change the speed to something like 90 & 20 for example, the object gets a little blurry and the values of the SDL_Rect x & y positions between updates jump for 2 or 3 units.
About the rounding hint, how would you round values? I found no round function in c++ libraries, only floor, ceil, fabs.

Or would you create a round function like this one I've found on the web?

double round(double x)
{
return ( (x - floor(x)) >= 0.5 ) ? ceil(x) : floor(x);
}

I know that lots of people do cool games with SDL. How do they handle this issue with precision?

What is the cool way to do this?

I'll make a search for fixed point math as you suggested. Thanks a lot!

[]'s
Kwizatz
Kwizatz
What about using milliseconds instead of seconds for your time unit? I recently did that on my engine, mostly because SDL_GetTime() returns ms, and I didnt see a good reason why I should be using anything else, not even animation interpolation presented a huge problem to switch, it just required a different way to get the computation.
Kwizatz
Kwizatz
In Reply to your PM:

You have this:
...#define TICK_INTERVAL 1000 / 60...while (!isDone){frameTime = SDL_GetTicks();if (nextTime <= SDL_GetTicks()){deltaTime = (frameTime - lastTime) / 1000.0f;checkInput(&gameEvent);update(deltaTime);nextTime += TICK_INTERVAL;lastTime = frameTime;}draw();}...


I don't like the nextTime part, it seems to me that it may produce some choppy movement, but anyway, I would change it to this:

while (!isDone){frameTime = SDL_GetTicks();checkInput(&gameEvent);update(frameTime - lastTime);lastTime = frameTime;draw();}


Actually that's very close to mine, obviously, you'd change update to take an Uint32 parameter instead of a float.

Then inside update you would calculate position exactly the same as you do now, except, you'd define speed in pixels per ms rather than pixels per second, you can keep speed an int, but then the slowest you can go is 1 pixel per ms, you can make it into a float and then go slower than that (a game cycle is almost never 1 ms so the chances of having 0.5 speed truncated to 0 are slim).

for animation purposes the same thing applies, you just have to convert your float seconds constants to int milliseconds.
rbento
rbento
Hello Rodrigo,

I did exactly as you said, but the results were even choppier. The object I use to test the movement is a simple ball sprite that moves and bounces on the screen edges.

Setting the speed to 1 makes it moves really fast, and changing the speed to float leads to the same problem as before: casting a float result to an int and loss of precision. The ball movement is still choppy, and some times, depending on the value you set to speed the ball stops moving because the result of the calc is 0;

   ...   while (!isDone)   {      frameTime = SDL_GetTicks();      checkInput(&gameEvent);      update(frameTime - lastTime);      lastTime = frameTime;      draw();   }   ...   ...   void Sprite::move(Uint32 timeInMilis)   {      positionX += (speedX * timeInMilis) * directionX;      positionY += (speedY * timeInMilis) * directionY;      rectDest.x = positionX;      rectDest.y = positionY;   }   ...


I've read a lot of articles, but no one could help me.

About that nextTime variable that you've mentioned, that was to regulate the update rate. I update() the game at a rate of 60 times per second and draw() as much as I can.

Removing that would make it update() at the same rate of draw() which is about 400 times per second.

That really does not affect the movement since it is time-based, so having it or not makes the ball move shaking anyway.

Is there anything else I could do? What do you think?



Kwizatz
Kwizatz
Well, you could try accumulating time, which is what nextTime did, but in a better way, try this inside update or replace accordingly on your game loop:

void update(Uint32 msdelta){static Uint32 accumulator = 0;accumulator+=msdelta;while(accumulator>TICK_INTERVAL){position = position + speed * TICK_INTERVAL;accumulator-=TICK_INTERVAL;}}
geolycosa
geolycosa
This might not be exactly what you're looking for, but the way that programmers have traditionally retained precision when working in fixed point environments (the old consoles, for example) is to bit shift a few times before performing computations, then bit shift back when you're finished, giving you a "sub pixel" calculation.

Will Miller | Game Designer | Big Huge Games
iaminternets
iaminternets
Regardless whether you fix your current choppiness, you're going to want to use a more precise timer than SDL's built-in functions, especially if you want to make anything network-enabled.

You'll need separate timing code for each platform, but that's unavoidable.
I love the 'nets.
rbento
rbento
I thank you all for replying. I'm learning a lot from it.

Now you guys have suggested the accumulator, the bit shift and a more accurate way to handle time than what SDL provides.

What path is the more indicated for a 2D game?

And where could I find more information about it?

Do you guys could point me some link or give me some example?

I really want to do it the best way possible!

I tried the SDL game Ace Slayer posted recently, and I see that the movement in that game makes the sprites a little blurry. Is that normal?

Thanks a lot!!!

[]'s
rbento
rbento
I was taking a look at some purely 2D games and the objects are a little blurry when moving fast, and I wonder what is the reason of this.

I assume that that is normal then.

But taking a look in some 2D / 3D games like Gunbound, I see that the fast object movements are really smoother.

This game seems to use a 3D technology but orthogonal view.

Does this makes sense? The movement of a '3D sprite' tend to be more accurate, since it's position data are stored in float data types.

I am thinking about using SDL for the window and input management and go for OpenGL with othogonal view for 2D.

Is that a good way towards a more accurate time-based sprite movement?

Hugs

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.