5

Cart #39165 | 2017-04-07 | Code ▽ | License: CC4-BY-NC-SA
5

Hi,

I'm working on my "Who dares" game (v0.13.3 for now) and now fight with bullets movements. Player and foes can move and shoot on 8 directions. To prevent all those sprites going faster on diagonals I just multiply their step value by 0.75, an approximation on cos(45) (or sqrt(2)/2, you choose) to get back to integers every 4 frames. Anyway, those sprites move look jagged on diagonals. I first found a way to fix this by copying Y decimals to X coord, but it's rather complicated and not the best way I think. Plus, it also messes with collisions.

So I went to basics: if movement is jagged because of float coords, how is drawn a float positioned pixel? I made the little cart here to experiment this. To make it short, a float pixel will be drawn at a floored, and not rounded, position. So pset(9.75,9.25) will put a pixel at 9,9. Fine with that, but further experimentations show bugs, as shown here.

The cart shows 6 different increment, named px, each one its own line. Every nth frames (fq value set on line 11) a value named x, initiated with 0, has px added, then printed at x position on light green if it's an integer, ie if x==flr(x), dark green otherwise.

Things go well when px=0.25, 0.5, 0.75 and 1: they turn light green when no decimal, the same moment they move 1 pixel right. Can't ask for more.
But things are not too good on first two lines (px=0.1 and 0.2):
x keeps being drawn with dark green, that means the x==flr(x) is always false, even when the value shown have no decimals.
When those values drop their decimal, they should move 1 pixel right. But they don't, it happens on the next increment. As if their no-decimal value is in fact lower than the integer.
While those steps only has 1 decimal, quickly their added value have decimals like 0.999, 0.998, etc. Quicker with px=0.1 (0-1, then 5 and >), later with 0.2 (33 and >)

My jagged movements probably have little to do with this bug and is more about using 0.75 factors and having X and Y that don't have their integer value changing at the same time. But well, I just discovered this and wanted to share.
Anyway if anyone knows how to simply have nice diagonally moving float positioned sprites, I'd really be happy to hear!

P#39166 2017-04-07 17:58 ( Edited 2017-04-09 14:20)

1

All numbers in PICO-8 must be an integer multiple of the smallest representable number, 2^-16. The problem is that "0.1" and "0.2" can't be represented exactly as multiples of 2^-16.

Choppy diagonal motion is probably happening when your sprite ends up at a fractional position -- let's say (0.75, 0). The sprite floors its coordinates and draws at (0, 0). When you move it diagonally to (1.5, 0.75), the on-screen position becomes (1, 0). The next frame it's (2.25, 1.5), then (3, 2.25), then (3.75, 3) -- that's (2, 1), (3, 2), (3, 3). The x and y coordinates moving out of sync causes choppy motion.

To fix it, you could try rounding the x and y coordinates any time you're not moving. Then you always start from a whole number, and the rounding cutoffs are at the same place for both coordinates.

P#39444 2017-04-09 06:48 ( Edited 2017-04-09 10:48)

Thank you ianh, you're right about the float, the resolution of 2^-16 is the point. Good to know. I'm still thinking too much in base 10 :)

Concerning choppy diagonal motion, I think rounding a bullet start point won't solve the problem. The problem mainly shows on the NE/SW diagonal: with a start point at (30, 50), the next step towards NE with 0.75 increment will be (30.75, 49.25). The bullet will show at (30, 49) which is already a problem.

I started making some drawings and found a somehow simple fix by incrementing only x, and using x to get y. Let's say a bullet starts at x0=30, y0=50:

• if the bullet goes NW, x & y both decrease. For SE, they both increase. So, as (y-x) keeps being 20 (ie y0-x0) all way long, bullet y will equal flr(x)+(y0-x0)
• if it goes NE, then x increases while y decreases. For SW, it's the opposite. So in both ways x+y=80 (ie y0+x0) and bullet y will be (y0+x0)-flr(x)

This way, only x manages factors (step, bult_speed, game_speed), which maybe goes a bit faster. But it's ok only as long as bullets keep going straight, and on strict 45° angles. An isometric game would need an adaptation (by having a 0.5 factor on y, I guess). And I had a bug with bouncing bullets, as I forgot resetting x0 and y0 each time the bullet bounces.

P#39447 2017-04-09 10:20 ( Edited 2017-04-09 14:20)

Hello, @ianh.

Even while this is 3 years after your reply on this thread, thanks to your tip I could make a demo which has consistent speed when going diagonals and no jittering. You can check it out here.

P#77320 2020-05-28 15:32 ( Edited 2020-05-30 21:09)

Thanks for sharing this demo! I was looking for specific details about the interaction of graphical functions with fixed-point values, and this gave me the exact information I was looking for!

P#103179 2021-12-20 15:26