Sprite collisions don't respect its adjusted center value

I'm experiencing an issue with sprite collisions using the Lua SDK on Mac. I have a simple bowling simulation in which I'm attempting to detect when the Ball (a sprite) enters the PinRack (that is, the boundary of the square area containing the pins, also a sprite). For convenience in laying out the pins, I've adjusted its center point so that it's situated at the head pin. (In retrospect, maybe that's not the right approach, but regardless it exposes this bug.) That all works as expected, as shown here:

The magenta lines are the collision rects drawn by the simulator. The cyan box is my own debug drawing of the collision rect for the rack (which uses getCollideRect and perfectly overlaps the magenta one for the pin deck in that screenshot). The cyan dot is the "center" point of the rack (at which it is positioned, and about which it rotates, as expected). Although drawn correctly, collisions seem to be misaligned, as though the rack sprite were actually centered at the head pin (that is, as though the head pin were equidistant from all sides of the rack, instead of at its front edge).

The PinRack has an update function which checks for collisions. The only object in the same collision group is the Ball. Here's the relevant excerpt — to illustrate the issue, I'm just aborting the moment a collision is detected, to enable me to capture a screenshot of the frame in which it occurs:

    local collisions = self:overlappingSprites()

    for i = 1, #collisions do
        local sprite = collisions[i]

        -- the ball has ented the pin deck
        print("PinRack Collided with ", sprite.className) -- always Ball
        assert(false) -- stop the moment the collision is detected

My expectation is that this should happen the moment the magenta box around the Ball intersects the magenta/cyan box around the PinRack. Instead, the intersection triggers early when the ball approaches the pins from the front (note that the magenta collision rects disappear upon the assertion, but I promise the collision rect for the ball hasn't changed!):

collision from the front

And late when approaching from behind (here I've rotated the rack 180º about its head pin "center"):

collision from the back

The ball appears to be either half-pindeck-size distance from it, or half-pindeck-size through it depending on the direction of entry. The observed behavior suggests that the rect actually being used in collisions is centered (truly) around the adjusted center, rather than respecting that adjusted center in order to align with the visual representation of the intended collide rect. This theory is reinforced by the fact that collisions with each side work as expected, given that the center point is still at the true center in that axis.

I’m reasonably confident in my expectation and my analysis, but please set me straight if I’m thinking about this wrong. Thanks!

Hmm, I did a little RTFM, and I see this callout in the docs for setCollideRect():

setCollideRect() marks the area of the sprite, relative to its own internal coordinate system, to be checked for collisions with other sprites' collide rects. Note that the coordinate space is relative to the top-left corner of the bounds, regardless of where the sprite’s center/anchor is located.

I suppose that means the behavior I'm seeing should be expected, though it was definitely surprising at first. I also suppose that means there is still a bug, but the bug pertains to the simulator's incorrect placement of the collideRect overlay for the sprite, which matches my expectation but does not match the actual collision implementation or the docs, at least for sprites with an adjusted center/anchor.

I could calculate the adjusted collideRect myself whenever I set a new center/anchor on a sprite, but is there a reason that this isn't done internally? Is there a performance implication? (I wouldn't think so, since it seems the correct offset could be precomputed when the collideRect or center is set.)

1 Like