Smoother Acceleration?

Started by
6 comments, last by metalmidget 15 years, 8 months ago
I'm coding a simple 2d space shooter in c++ using SDL and I'm having a problem with my ships acceleration atm. I calculate the velocity using an acceleration constant + time from my own timer class. The velocity is then rounded up to the nearest pixel and I add the velocity to my ship's Y location. However, doing it this way makes the acceleration very choppy since it only occurs at whole numbers and isn't the effect I'm after. I'm pretty sure there is a better way to do this so it always increments by 1 pixel but at a different rate but I don't know how to go about achieving this. Help is much appreciated.
Advertisement
Do all of your physics calculations in floating point and then round the positions every frame to whole numbers when they are drawn
That's what I'm doing but it's a very slight acceleration (I want it that way) so it keeps rounding to 1 for a long time then 2 etc.
You are basically looking for sub-pixel precision. Most 2d graphics APIs (e.g. SDL) don't support this, unfortunately.

If you can change your rendering code to use OpenGL (through SDL), you will be able to render objects at fractional pixel positions (the API will take care of the details). However, depending depending on your game's design it might be difficult to make such a change. In that case, maybe some bright mind here can think of an algorithm to achieve this on a plain 2d API.

Anyone? ;)

[OpenTK: C# OpenGL 4.4, OpenGL ES 3.0 and OpenAL 1.1. Now with Linux/KMS support!]

Alright, thx for your reply.
Quote:The velocity is then rounded up to the nearest pixel and I add the velocity to my ship's Y location.


Don't round your velocity. Only round the position variables, and only for the purpose of rendering.
For example, say the ship starts at (250, 100), with zero velocity. You said you only wanted small, subtle accelerations, so the accel might only be, say, (.3, .7).
Just so can see what I mean, the first few frames would give results like this:
|Acceleration | Velocity   | Position       | Position Rendered To|-------------------------------------------------------------------| (0.3, 0.7)  | (0.3, 0.7) | (250.3, 100.7) | (250, 101)          || (0.3, 0.7)  | (0.6, 1.4) | (250.9, 102.1) | (251, 102)          || (0.3, 0.7)  | (0.9, 2.1) | (251.8, 104.2) | (252, 104)          || (0.3, 0.7)  | (1.2, 2.8) | (253  , 107  ) | (253, 107)          || (0.3, 0.7)  | (1.5, 3.5) | (254.5, 110.5) | (255, 111)          |-------------------------------------------------------------------

cheers,
metal
Just to be sure there is not a misunderstanding, it's the perceived difference in speed that is choppy, not the actual ship positions, right? That is, while accelerating, a ship moves 1 pixel per frame for a while and then 2 pixels per frame?
If that is your issue, then you are rounding too early and should calculate the position, not only the acceleration using floating point math.

However, if the ships are really moving so slow that they only move a pixel every second or so, then you indeed need sub-pixel rendering. Since you already use SDL, you should have not too many difficulties with that, just render your sprites as textured quads (this will be done using OpenGL) instead of blitting them. It's the same thing, only a bit different setup and a few different commands.

If you want to (or have to) do without OpenGL, you can do sub-pixel accuracy manually, too. One way to do it is to abuse scaling. It is not the most efficient way, but it's easy to understand, and modern computers are fast.
If the background (space) is just black, it's easiest, so I'll use that assumption to explain. It works with a non-black background too, however.
Give your ship sprite a 1-pixel black border and scale it to four times its size. That will take about 16 times as much memory, but sprites are small, so that's acceptable. When you blit your sprite onto the screen, it has to be scaled down again, giving the same visual result as before. Usually, image scaling happens with some kind of filter (make sure you choose "quality" scaling). When you scale up your sprite, it doesn't matter, but it's what you want when it's scaled down.
The trick is, if you offset the source rectangle (in the big sprite) by one pixel, it will correspond to 1/4 pixel on the screen (because the sprite is scaled down), and the filtering will provide the sub-pixel accuracy by "mixing" every pixel with the surrounding ones so it has the overall weight it should have.
Another way is to store your velocities and positions at (say) 16x their actual values, in ints.

Then, just as you render, shift the x,y values right (say) 4 bits.

e.g.:

To move:
x += vx;
y += vy; /* this is as usual... */

/* but, to draw... */
draw_it(x >> 4, y >> 4);

Well, addition/subtraction are easy, esp. if you don't have to worry about overflow (in case you're lucky and know the ranges fit.) Multiplication/division or other math can be more trouble than it's worth though.

But, these days, floats are probably easier and not much, or any slower.
I'm not sure if I'm missing something here but the methods you guys are giving seem to be needlessly complicated... The way I read the OP, there's nothing wrong with the movement, it's the jump in velocity from 1 pixel/sec to 2 pixels/sec etc etc that looks choppy.
That being the case, what's wrong with what I posted? Simply don't round any of your variables. Only round the numbers that you pass into your blitting/drawing function.

cheers,
metal

This topic is closed to new replies.

Advertisement