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:
The filled rect should be aligned with the grid. But when moved partially off screen you can see the rectangle overhangs by 1.
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)
Just following up here so I can remove this from my very old todo list. 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.
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.
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.