Pixels are either on or off. The minimum amount you can move a sprite is a single pixel. So how you make the sprite move slower than 1 pixel per frame?
The way I did it was to add the speed to a variable and test if it had reached 1 (or -1). If it did, then I would move the sprite and reset the variable to 0, like so:
update(dt):
temp_dx += speed * dt
temp_dy += speed * dt
if (temp_dx > 1)
move sprite
reset temp_dx to 0
if (tempy_dy > 1)
move sprite
reset temp_dy to 0
I disliked this approach because it feels silly and the sprite's movement looks very jerky. So in what way would you implement sub-pixel movement?
Answer
There's a number of options:
Do as you do. You've already said it doesn't look smooth. There are some flaws with your current method though. For
x
, you could use the following:tempx += speed * dt
while (tempx > 0.5)
move sprite to x+1
tempx -= 1
while (tempx < -0.5)
move sprite to x-1
tempx += 1this should be better. I've switched the if statments to use 0.5, as when you're passed 0.5 you're closer to the next value than the previous. I've used while loops to allow for movement of more than 1 pixel per time step (it's not the best way to do it, but it makes for compact and readable code). For slow moving objects it'll still be jumpy though, as we haven't dealt with the fundamental issue of pixel alignment.
Have multiple graphics for your sprite and use a different one depending on the subpixel offset. To do subpixel smoothing only in x, for example, you could create a graphic for your sprite at
x+0.5
, and if the position is betweenx+0.25
andx+0.75
, use this sprite instead of your original. If you want finer positioning, just create more subpixel graphics. If you do this inx
andy
your number of renderings can quickly balloon, as the number scales with the square of the number of intervals: a spacing of0.5
would require 4 renderings,0.25
would require 16.Supersample. This is a lazy (and potentially very expensive) way of creating a image with subpixel resolution. Essentally, double (or more) the resolution at which you render your scene, then scale it down at runtime. I would recommend care here, as performance can drop quickly. A less agressive way of doing this would just be to supersample your sprite, and downscale it at runtime.
As suggested by Zehelvion, it may be that the platform you are using already supports this. If you are allowed to specify non-integer x and y co-ordinates, there may be options to change the texture filtering. Nearest-neighbour is often the default, and this will cause "jerky" movement. Other filtering (linear/cubic) would result in a much smoother effect.
The choice between 2. 3. and 4. depends on quite how your graphics are implemented. A bitmap style of graphics suits pre-rendering much better, whereas a vector style may suit sprite supersampling. If your system supports it, 4. may well be the way to go.
No comments:
Post a Comment