Collision detection failing between sprites at negative Y coordinates

I’m working on a top-down scrolling game where the camera follows the player, who starts at the origin 0, 0 and can move any direction from there (using playdate.graphics.setDrawOffset() similar to how the camera works in the Level 1-1 example, but on both the X and Y axis).

I’m seeing collision detection intermittently fail between the player sprite and other sprites when they are both at negative Y coordinates (i.e. above/north of the 0, 0 origin point). Using “show sprite collision rects” in the simulator shows the rects as expected, but calling overlappingSprites() on both sprites sometimes returns an empty array on just one or both of them even if they visibly overlap. It also occasionally returns a detected overlap on both or only one of the sprites even when they are not overlapping.

If I simply offset all the starting coordinates in my game by +1000 (thereby avoiding having any sprites at negative Y positions), everything works as expected with no issues.

Is this a bug, or is the SDK collision system intended for use with positive coordinates for the whole gameplay area? (Doing so isn’t a problem in my case, just trying to figure out what the best practice is. If it’s a bug, I can make a reduced test case)

SDK 1.12.0 (update check says it’s up to date) on MacOS 12.4. The issue persists on the physical Playdate running OS 1.12.1.

Hello,

That does sound like a bug.

However, I haven't been able to reproduce what you're seeing yet. I put together a little sample project, which I'll attach here - maybe you can tell me if it looks like I'm doing something in a different way than you are?

(use the D-Pad to move the player around, press A to test for overlapping sprites, press B to switch between "freeze" and "overlap" collision response modes - I wanted to test moveWithCollisions() as well)

CollisionBug3.zip (4.4 KB)

If you have any code that you can share that demonstrates the problem you're seeing, it would be very helpful!

Using that sample project, I narrowed it down to only when a sprite’s collide rect is different from its bounds. If I change line 23 to block:setCollideRect(2, 2, blockW - 4, blockH - 4) it starts happening.

Exact steps to reproduce using that code change:

  1. Have the collisions in overlap mode and the simulator showing collision rects
  2. Press A to print the “baseline” number of overlapping sprites (some sprites may randomly already overlap)
  3. move the “player” to overlap with the collision rect of one of the sprites above the origin
  4. Press A again to print the updated number of overlaps.
  5. If the number of overlaps did increase from step 2 to step 4, move the player slightly (or to a different sprite) and try pressing A again. If unable to reproduce the bug, rerun the code to re-randomize positions.

Expected result: the printed number of overlapping sprites in step 4 should always be 1 higher than the number from step 2.

Actual result: sometimes, the number of overlapping sprites printed in step 4 is the same as the number from step 2.

The bug only seems to affect calculation of overlapping sprites; the collision still registers and moveWithCollisions() appears to be unaffected by the bug.

It seems to occur more often when the player is overlapping with a corner of the sprite, but sometimes occurs when overlapping other areas as well. For example in this screenshot, the overlap between the player and sprite 7 doesn’t seem to register when the player is in the exact position shown, but if the player is moved to the right slightly, it registers. However, it’s very inconsistent and sometimes takes some fiddling to find a sprite/area that demonstrates the issue.

Also, I just updated to SDK 1.12.2 and the issue still occurs.

I’ve also tried using a fixed random seed so that theoretically it would be the same across runs, and the overlap detection behaves differently each time I run it – on some runs it detects the overlap with the player in a particular position, but other times it doesn’t detect it even with the player and all other sprites in the same positions.

Very strange! I have been trying for a while now but I do not seem to be able to reproduce what you're seeing - it's consistently returning the correct results for me in a nice deterministic way. The fact that the results vary when calling the same function with the same setup for you is just super strange, I can't think of a good reason that would be happening.

I'm going to keep playing around to see if I can see any random or incorrect behavior... I'd love to see a video of what you're seeing if that's possible, maybe you're doing something I'm not, though I don't know what that could be!

I modified the code so that it calls gfx.sprite.allOverlappingSprites() each frame and draws the count in the upper left corner, to make it easier to see if the count changes when it should. Here’s a video where you can see the overlap count change unexpectedly as the player moves around within another sprite’s collision rect.

And the version of the project used for that video, including the compiled PDX in case that makes any difference:

CollisionBug3.zip (26.7 KB)

Seemingly, calling gfx.sprite.allOverlappingSprites() every frame makes the issue occur more often than simply calling it on an occasional button press. With this version of the project, just about every run has at least one sprite that has wonky overlap detection.

And here’s a version of the project where I set a fixed random seed and can get different results on multiple runs despite all the sprite positions being identical:
CollisionBug fixedSeed.zip (27.4 KB)

Video of me doing multiple runs of this version, with different results when moving over the same sprite. In some runs, the entire sprite registers the overlap correctly; in other runs, only parts of it do.

Seems like maybe the “entry point” where the player first contacts a sprite changes which other areas of that sprite are able to register the overlap? But even that doesn’t seem to work consistently.

Thank you very much for the extra info and videos! I was able to reproduce what you were seeing, and I think I've finally been able to come up with a fix. (it looks like it was mostly caused by a problem with a hash function used by the collision system, so that was fun to track down! The way you move the sprite around before calling overlappingSprites() turned out to have an impact on when the bug would show up).

I'll get the fix included in the next SDK release, but if you're interested in testing it out earlier I'll figure out a way to get a beta SDK to you, just let me know!

Thanks for all the time you spent on this! Sure, I wouldn’t mind testing the beta SDK if it’s not any inconvenience (if it is, don’t worry about it – I don’t want to waste any more of your time on a bug that I already have a workaround for).