This is on:
- macOS 12.3.1
- SDK 1.10.0
Context
I'm making a game that plays on a grid with a cursor you can move around. When the player taps on the d-pad, the cursor animates between positions. Not only that, but the "color" of the cursor changes based on what square the cursor's on, like on a checkerboard. I want to animate both the cursor's position and its color.
The bug occurs when the color animation outlasts the position animation.
The Code
When the user taps a direction, I create animators for both the position and the color. I use sprite:setAnimator
for the position, but have to use my own property for the color animator. (Math omitted for brevity)
function Cursor:movePosition(newX, newY)
// ...
local anim = gfx.animator.new(positionTransitionDuration, oldPos, newPos, playdate.easingFunctions.linear)
self:setAnimator(anim)
// ...
self.colorAnimator = gfx.animator.new(colorTransitionDuration, fromColor, toColor, playdate.easingFunctions.linear)
end
Then, in sprite:draw
I use the colorAnimator
I set to change the dithering pattern I draw with. When that animator ends, I save it to the sprite's image to avoid re-drawing. In sprite:update
, I see if the sprite's been moved -- if it has, I remove the image and mark it as dirty so the color can animate again.
-- Compute x/y based on position, re-draw if it's moved.
function Cursor:update()
local x = self.gameRect.x + self.position.x * self.xStep
local y = self.gameRect.y + self.position.y * self.yStep
if x ~= self.x or y ~= self.y then
self:setImage(nil)
self:markDirty()
end
end
-- Draw a black border with dithering
function Cursor:draw(x, y, width, height)
local image = gfx.image.new(width, height)
gfx.pushContext(image)
gfx.drawRect(x, y, width, height)
local ditherValue = self.colorAnimator:currentValue()
gfx.setDitherPattern(ditherValue)
gfx.fillRect(x, y, width, height)
gfx.popContext()
-- Always draw. Without it, it flickers in the last frame.
print("Drawing " .. ditherValue)
image:draw(x, y)
-- If we're done animating, then save the image.
if self.colorAnimator:ended() then
print("Preserving " .. ditherValue)
self:setImage(image)
end
end
Messing with Animation timings
If both the position and color animations take the same amount of time, it works great.
positionTransitionDuration = 500.0
colorTransitionDuration = 500.0
However, if I want the color animation to occur more slowly -- to continue changing color after the cursor as finished moving, it just... stops changing color as soon as the position animation ends
positionTransitionDuration = 500.0
colorTransitionDuration = 2000.0
It looks like
sprite:update
just stops being called once the animator set insprite:setAnimator
ends.
Test Project
You can try it for yourself with the attached code. Thanks!
test-custom-drawing-sprite.zip (101.1 KB)