Playdate.sprite with odd-dimensioned images drawn off-by-1 when x,y position offscreen

I just discovered a bug with the default :setCenter(0.5,0.5) sprite positioning behavior. If a sprite is offscreen (anywhere x < 0 or y < 0) then the sprite is drawn off by one pixel (x+1, y+1 or both) from its expected position.

A workaround is to :setCenter(0,0) on every sprite, but since :setCenter(0.5,0.5) is the default, sprites palced partially off screen are mis-drawn by default.

Observed version: Playdate SDK 1.12.3 (2022-08-16) for Mac.

Here's a demo Gif:
demo
The filled rect should be aligned with the grid. But when moved partially off screen you can see the rectangle overhangs by 1.

And a reproduction repo:

4 Likes

I just looked in your repo and noticed that the box you draw is 81 by 81 pixels. When :setCenter(0, 0) is used the origin of the box is the top left corner which is (0, 0) in relation to the box. When :setCenter(.5, .5) is used the origin of the box is suddenly (40.5, 40.5) which is not so round. Since pixels must be set at integer coordinates, I would imagine that there is some rounding going on.

I know practically nothing about the Playdate SDK, but just off the top of my head this doesn't sound like a bug in the SDK, but a nice old fashioned rounding "error" because of the odd box dimensions.

@peterpop You appear to be correct. The issue is limited to when a sprite has an odd-sized sprite backing image. I've updated the title to reflect this.

I can't stop thinking that there is something odd (pun intended) about your example.

First the fact that your box is 81 by 81. But I also noticed that you specifically place it off-center box:moveTo(41,41)

That is not quite the center of an 81 by 81 box. The correct center would be (40.5, 40,5). I tried that in your example and that actually works. The box follows the guides without gaps.

Anyway, I can understand why you're puzzled by it. If for example the box (still 81 by 81) is located at (41, 41) its left edge would be at (41-40.5)=0.5. Assuming rounding down we get 0. For the right edge we add the width (0+81)=81. That fits what you see on the screen when x or y => 0.

Lets put the box at (1, 41) (With your move logic we always move in increments of 20). Now the left edge will be at (1-40.5)=-39.5. Rounded down is -40. Right edge would then be (-40+81)=41. This does not match what you see.

However! I am pretty sure the sprite API is implemented in C. And maybe the API developers simply casts the floats to ints before blitting to the screen.

For the x => 0 values before: left edge is at 0.5. Cast to int is 0. Right edge is at 0+81=81. No difference. Everything checks out.

For x < 0: Left edge is at -39.5. Cast to int is -39. Right edge is at -39+81=42. That is precisely what we see.

So I think what you see on screen is an artifact of casting float to int in C. Whether it's a bug or not, I'm not so sure. As mentioned you can fix it by placing the box at it's actual center (40.5, 40.5)

1 Like

Just following up here so I can remove this from my very old todo list. :sweat_smile: This issue came up again recently (Sprites seem to run "out of sync" or skip update calls? - #3 by tkers) and I was going to say I have a fix in for it but it looks like I never got around to implementing it. I'll do that now and get it in the queue.

Sorry it fell off the radar!

5 Likes

I wanted to circle back and see if this was ever fixed (I think not).

Hilariously I accidentally recreated this issue again on a different project. :stuck_out_tongue:

I added a debug grid and immediately saw the off by one near the upper left corner. Same fractional positional (e.g. x = 10.5) with the default :setCenter(0.5,0.5) but with an even sized backing images. I fixed my x/y positions to only use integers and everything is fine, but thought I'd bump this.

Pretty sure @peterpop's assessment is correct and it's a casting / rounding bug.

@dave if you think this should have been already been fixed, let me know know and I'll whip up a test case to help you reproduce the issue.

We've got a fix in the queue but I have no idea when it'll make it into a release. We've got a huge backlog right now. :playdate_pensive: Soon, I hope!

2 Likes