I have a sprite that rotates as the user turns the crank and travels in that direction. It's only travelling at a speed of 1 pixel per update.
When I disable the rotating of the sprite, collisions seem correct.
However, when I enable the rotation it goes what I can only describe as bat-shit crazy.
The collision response function is:
function Harry:collisionResponse(other)
if (other:isa(Crop)) then
other:remove()
return collisionType.overlap
else
return collisionType.freeze
end
end
What's going on here? It's driving me mad.
[quote="BanksySan, post:1, topic:20755, full:true"]
I have a sprite that rotates as the user turns the crank and travels in that direction. It's only travelling at a speed of 1 pixel per update.
When I disable the rotating of the sprite, collisions seem correct.
However, when I enable the rotation it goes what I can only describe as bat-shit crazy.
The collision response function is:
function Harry:collisionResponse(other)
if (other:isa(Crop)) then
other:remove()
return collisionType.overlap
else
return collisionType.freeze
end
end
I get similar behaviour with slide and bounce collision types.
Hi!
The problem is that you're not adjusting the sprite's collideRect to account for the sprite's increase in size when rotating. The collideRect is always at the top-left of your sprite's bounding box. Rotation is pushing this through your wall, causing the collision to resolve with the sprite on the other side.
Here's your sprite with a border and Show Collide Rects to illustrate:
You could do this to adjust the size of the collideRect when rotating:
local _, _, w, h = self:getBounds()
self:setCollideRect(0, 0, w, h)
But that doesn't fix the issue. Your player can still tunnel through walls by rotating. And you probably don't want a variable-sized sprite anyway.
So to maintain the sprite size and offset the position of the collideRect, try:
if (self.enableRotation) then
self:setRotation(crankAngleDeg)
local _, _, offsetX, offsetY = self:getBounds()
offsetX, offsetY = offsetX * 0.5 - 16, offsetY * 0.5 - 16
self:setCollideRect(offsetX, offsetY, 32, 32)
end
Because the collideRect is at 0,0 in the sprites bounding box. So if the size of the bounding box changes (as it does when you rotate the sprite), the position of the collideRect moves.
When you rotate your sprite, this rotation shifts the collideRect (the red square) to a position that is overlapping your walls:
Then you do moveWithCollisions which resolves the overlap in an unexpected (to you) way.
Adjusting the collideRect during rotation so it is centred on the sprite rather than at 0,0 fixes this.
-- Setup here, not interesting.
--- Generate an image at set angle in degrees
--- @param angle number The angle in degrees
--- @return playdate.graphics.image
function generateImage(angle)
local vectorOffset = geo.vector2D.new(16, 16)
local vectorA = geo.vector2D.newPolar(16, angle) + vectorOffset
local vectorB = geo.vector2D.newPolar(16, angle + 120) + vectorOffset
local vectorC = geo.vector2D.newPolar(16, angle + 240) + vectorOffset
local image <const>
= gfx.image.new(64, 64)
gfx.pushContext(image)
gfx.drawPolygon(vectorA.x, vectorA.y, vectorB.x, vectorB.y, vectorC.x, vectorC.y)
gfx.drawRect(0,0,32,32)
gfx.popContext(image)
return image
end
class('Player').extends(gfx.sprite)
function Player:init(x, y, speed, enableRotation)
self.speed = speed
self.enableRotation = enableRotation
self.collisionResponseIndex = 1
self:setImage(generateImage(0))
self:setZIndex(0)
self:setCollideRect(0, 0, 32, 32)
self:moveTo(x, y)
self:add()
end
function Player:update()
-- Movement calculations here
local crankAngleDeg = pd.getCrankPosition()
local crankAngleRad = math.rad(crankAngleDeg);
if (self.enableRotation) then
self:setImage(generateImage(crankAngleDeg))
end
-- Do movement here
end
function Player:collisionResponse(_)
-- Handle collision
end