Lerp and moving sprites

Hi everyone!

I'm really struggling to find any examples of Lerp usage and how to move objects around. Has anyone got good examples or point to docs that have sprites moving around utilising the lerp function?

My implementions seem to over shoot, stutter or never get there. Feels very basic so sorry for asking, but moving sprites smoothly seems like something that should be covered somewhere‽

Here's a simple implementation using a class. You can of course have this logic outside the sprite too.

You need a couple of variables:

  • Position before the movement.
  • Target position.
  • Normalized transition progress, aka "t".

Your class could then look something like this:

class('GamePiece', {
    startPos= playdate.geometry.vector2D.new(0, 0),
    targetPos = playdate.geometry.vector2D.new(0, 0),
    transition = 0.0
}).extends(playdate.graphics.sprite)

GamePiece:init()
   ...
end

GamePiece:startMovingTo(x, y)

   -- Update starting position
   self.startPos.x = self.x
   self.startPos.y = self.y

   -- Set target
   self.targetPos.x = x
   self.targetPos.y = y

   -- Reset transition
   self.transition = 0.0

end

GamePiece:update()

   --  Only advance if we haven't reached the target yet
   local shouldMove = self.x ~= self.targetPos.x and self.y ~= self.targetPos.y

   if shouldMove then

       -- Move towards the target.
       -- Note that this example will reach the destination in fixed time, but is framerate dependent.
       self.transition = math.min(self.transition + 0.1, 1.0)      

       if self.transition >= 1.0 then
           -- We have reached the target
       end

       -- Move the sprite.
       -- You can also easily replace lerp with one of the easingFunctions.
       self:moveTo(
            math.lerp(self.startPos.x, self.targetPos.x, self.transition), 
            math.lerp(self.startPos.y, self.targetPos.y, self.transition)
       )
   end

end

GamePiece:draw()
   ...
end

You would then call e.g. knight:startMovingTo(200, 160) and the sprite would reach the new position in 10 frames.

Hope this helps! :playdate_happy:

1 Like

Thanks @outgunned ! I like the class extension idea! I went a made something work well enough after I posted this and got this below. I wanted it to use deltatime.... ANDDDDDD I thought maybe timers would work... so this is what i got:

function lerp(a, b, t)
  return math.ceil(a * (1 - t) + b * t)
end

-- start = {x,y}, end = {x,y}
-- return {x,y} 
function lerpVector(xyStart, xyEnd, time)
	return {
		x = lerp(xyStart[1],xyEnd[1], time),
		y = lerp(xyStart[2],xyEnd[2], time)
	}
end

function lerpTimer(timer, from, to, spriteObj)
    -- get new position for this frame
	local newPos = lerpVector(
		from, 
		to, 
		timer.value
	)
    -- actually move the sprite
	spriteObj:moveTo(newPos.x, newPos.y)
end


local t = playdate.timer.new(1000, 0, 1, pd.easingFunctions.outQuad)
t.updateCallback = function(timer)
	lerpTimer(
		timer,
		{ fromSprite.x, fromSprite.y },
		{ toPos.x, toPos.y },
		fromSprite
    )
end

fromSprite.t = t

My hope was I could reuse timers to strta new movement... but would have loved the above to be much more succinct

1 Like