Hi all,
I wanted to report this bug (or limitation, perhaps) with the SDK collisions system.
Context
The behavior was being reported causing a strange bug in my game. When standing by the top-right or top-left corner of a block, the player was not able to move horizontally above that same block, unlike what was expected from a normal ground/slide collision.
Here is an example of the bug from the game:
You can see that while pressing the "Left" key the player is not able to move left.
(...and yes, the player also has the left key :D)
The Bug
With a bit of digging I found that the bug is caused by the SDK collisions and the way it calculates the "Slide" vector.
When the bug occurs, there are two (important) collisions happening:
- The first collision is with the "corner block", since the player's movement vector is pointing both left (horizontal movement) and down (gravity)
- The second collision is with the wall below the player, again due to the downwards movement simulating gravity.
Both have collision type Slide.
The issue is that with the first movement, given that the exact location of the player's bottom-left coincides with the "corner block"'s top-right, the SDK collision system selects only one out of the two directions and the horizontal movement is cancelled. This can be seen with the "normal" of the collision.
The second collision, the "wall", then cancels the vertical movement, leaving the player standing still.
In other words:
- Step 0: The Player attempts to move bottom-left using SDK collisions
- Step 1: The "Corner Block" (Wall-2), only keeps vertical movement and sets horizontal movement to 0.
- Step 2: The bottom "Wall" (Wall-1) sets the vertical movement to 0, there is no horizontal movement to otherwise keep.
I've created the above high-definition diagram to represent my understanding of the bug.
My Work-around
Details
In case you come across this (which I'm sure you won't), what I did was to detect the situation with three variables:
isTouchingGround
- this already existed for obvious reasons.isHorizontalCornerBlock
- detects the horizontal collision in a corner.
Set these variables to false
before your checking your collisions, and then for each collision that occurs, check for this edge case scenario.
Before checking collisions:
local isTouchingGround = false
local isHorizontalCornerBlock = false
For each collision:
isTouchingGround = isTouchingGround
or (collision.normal.y == -1
and collision.type == playdate.graphics.sprite.kCollisionTypeSlide)
isHorizontalCornerBlock = isHorizontalCornerBlock
or (collision.normal.x ~= 0
and collision.type == playdate.graphics.sprite.kCollisionTypeSlide
and collision.otherRect.y == collision.spriteRect.y + collision.spriteRect.height)
-- -- You can directly save the reference to the sprite to the variable:
-- if isHorizontalCornerBlock == true then
-- isHorizontalCornerBlock = collision.other
-- end
If both variables are true by the end of the collision check, I disable the collisions for the block in the horizontal corner until the next frame. This will allow the player to move out of the "stuck" position and then the game continues normally.
if isHorizontalCornerBlock and isTouchingGround then
--print("Found horizontal corner block - disabling collisions.")
-- Reference to the horizontal corner block
isHorizontalCornerBlock:setCollisionsEnabled(false)
-- Schedule reset for collisions
playdate.frameTimer.new(1,
function()
--print("Re-enabled collisions")
isHorizontalCornerBlock:setCollisionsEnabled(true)
end)
end
For extra efficiency, you can also add a check for horizontalCornerBlock:collisionsEnabled()
to avoid creating multiple timers at once and extract the function out and simply reference it so it only gets created in memory once.
I hope this articulates the "bug" / limitation clearly. Crossing fingers I'm not the only who experiences this or that this helps someone
In case it would be helpful, I'm available to put together a minimum-code demo so don't hesitate to reach out!