sprite:setScale seems to move the sprite position

Hey,

I'm playing with the SDK and the simulator, and testing this code;

import "CoreLibs/graphics"
import "CoreLibs/sprites"

local gfx <const> = playdate.graphics
local sprite = nil

function myGameSetUp()
   local spriteImage = gfx.image.new("images/mysprite.png") -- this is 300px x 300px image
   sprite = gfx.sprite.new()
   sprite:setImage( spriteImage )
   sprite:setCenter( 0.5, 0.5 )
   sprite:moveTo( 200, 120 )
   sprite:add() 
end

myGameSetUp()

local initialDistance = 400
local distance = initialDistance

local moveTwo = false
function playdate.update()
   distance = distance + 5
   sprite:setScale(initialDistance / distance)
   gfx.sprite.update()
end

I expected that the sprite will get smaller, keeping its position on the center of the screen, but it seems to go right-down corner of the screen.

Am I missing something ?

This works as expected.

function playdate.update()
   distance = distance + 5
   sprite:setScale(initialDistance / distance)
   sprite:setCenter(0.5, 0.5)
   sprite:moveTo(200, 120)
   gfx.sprite.update()
end

But I do not understand why I need to do setCenter and moveTo again.
What does exactly sprite:setScale() do ?

@Dave Is this related to the issue we have, where repeatedly calling :setImage() on a sprite moves it, if the image is of a different size?

@skoji take a look at Single File Examples/spritescaling.lua where they setSize the sprite after scaling.

1 Like

@matt
Thank you for your advice.

In Single File Examples/spritscaling.lua, setSize() is used for sprite whose image is not set. The sprite in my code has an image set, and I can't setSize it.

As mentioned above, it works if you do setCenter and moveTo the sprite after scaling. However, I don't see why it works.

1 Like

This goes all the way back to when I first implemented the sprite system. Instead of doing the sane thing and storing a position (x,y) and center offset for sprites I thought it would be clever to instead store only the bounds because that's all the display list cares about. That's caused us all kinds of trouble.. When you scale the sprite it computes the sprite's position from the bounds and center x/y parameters, and that introduces a rounding error. Add those errors up and the sprite drifts. :playdate_agh:

I've filed a bug to go back and add an actual solid position for the sprite that only moves when you tell it to.

4 Likes

Potentially off-topic, but another "issue" I noticed with sprite:setScale is that it scales with subpixels, i.e. if a sprite is scaled such that it doesn't end in a whole number amount of pixels some of the pixels appear to disappear and not get sampled/rendered. (in my case, a 16x16 sprite scaled by 1.3 becomes 20.8x20.8 and renders improperly)

I fixed this by calculating the nearest (floored) whole number of pixels and finding the scale modifier that would result in that. (a 16x16 sprite scaled by 1.25 results in a 20x20 sprite and renders as you'd expect, or at least a lot closer) Doable, but kind of annoying for scaling animations. Unsure if any other workaround exists but an option to toggle this or perhaps different sprite sampling methods could be nice.

As for the sprite moving issue, @skoji's solution works for me as well.

Edit: After further testing, the 20x20 still isn't quite what I see when I nearest-neighbor scale up my sprite in an image editor.

1 Like