How do I draw a sprite over rects/circles or other sprites?

Hello everyone,

I have a sprite (black, white and transparent pixels) and want it do be drawn over any other objects in my scene. I tried setting the z-Index of my sprite, tried changing around draw statements for my different components but no luck. I am pretty new to Lua and the SDK but I couldn't find an answer to this yet.

More information:
The main sprite has an animation loop and looks something like this (shortened for clarity):

Sprite Code
import "CoreLibs/sprites"

local gfx = playdate.graphics
local geo = playdate.geometry
Cat = {}
Cat.__index = Cat

function Cat:new()
    local catImageTable = gfx.imagetable.new("/images/catsprite-extended-table-32-32-2.png")
    assert(catImageTable)

    local self = gfx.sprite.new()
    self:setZIndex(10000)
 
    self:setScale(3)
    self.getAlwaysRedraw()

    self.loop = gfx.animation.loop.new(200, catImageTable, true)
    self.loop.startFrame = 1
    self.loop.endFrame = 4

    function self:update()
        self.x = self.x + self.dx
        self.y = self.y + self.dy

        self:moveTo(self.x, self.y)
    end


    function self:draw()
        local newImage = self.loop:image()
        newImage:setInverted()
        assert(newImage)
        gfx.setColor(gfx.kColorBlack)
        self:setImage(newImage)
    end

    return self
end

The main sprite has a animation loop and looks something like this:

Main Code
import 'cat'
import "CoreLibs/sprites"
import "CoreLibs/graphics"

local gfx = playdate.graphics
local width = playdate.display.getWidth()
local height = playdate.display.getHeight()

catSprite = Cat:new()
catSprite:moveTo(100, height - 32 * 1.5)
catSprite:add()

function drawField()
    local widthScoreA = scoreFont:getTextWidth(scoreA)

    gfx.drawText(scoreA, width / 2 - 26 - widthScoreA, 10)
    gfx.drawText(scoreB, width / 2 + 20, 10)
    gfx.setColor(gfx.kColorWhite)
    gfx.setLineWidth(4)
    for i = 0, 8 do
        gfx.drawLine(width / 2, i * 28, width / 2, i * 28 + 14)
    end
end

function playdate.update()
    gfx.sprite.update()
    drawField()
    catSprite:draw()
end

The sprite looks like this, the grey pixels are transparency because it's a screenshot:
image

I wasn't allowed to add more than one picture so I hope this I enough for now.

I am working with SDK in version 1.9.0 on my Mac with the simulator.

I bet I missed a lot of details so feel free to ask questions.

To draw a sprite on top of everything you should

  • Call gfx.sprite.update() to update and draw sprites (which you do),
  • Add the sprite with gfx.sprite:add() (which you also do),
  • Give the sprite the highest z index with gfx.sprite:setZIndex(…) (still on track),
  • Not draw anything after calling gfx.sprite.update(). If you want to have other elements drawn under the cat sprite you can draw them onto other sprites with lower z indexes or use gfx.sprite.setBackgroundDrawingCallback to draw them under any sprite. Anything drawn after calling gfx.sprite.update() will be drawn on top of the sprites.
1 Like

Thanks for the quick help! I have a few questions still:

How do I draw a rectangle or other geometry on a sprite?:
I currently use these methods: gfx.drawLine() and gfx.fillRect() for drawing specific parts of the image. But they draw directly onto the canvas from what I understand. If I have these in my self:draw() function and don't call them in playdate.update() they are not updated.

Use lockFocus to draw to an image rather than the screen buffer.

Docs: https://sdk.play.date/1.9.1/Inside%20Playdate.html#f-graphics.lockFocus

eg.

playdate.graphics.lockFocus(imgSpriteBackground)
playdate.graphics.drawLine(10,10,20,20) -- line drawn to imgSpriteBackground
playdate.graphics.unlockFocus()

1 Like

If using the sprite draw callback seems like the right way to go in your case (if you'd be redrawing the sprite every frame, for example) check out the Asheteroids demo in the Examples folder of the SDK.

:man_facepalming: I realize now I didn't put any comments in there to explain what's going on. But maybe it'll be useful?

Thank you matt, So I finally got the time to work on it again and this was actually the solution to my main problem. I was able to draw a line in the background with my character being in front. Thank you :slight_smile:

1 Like

Thanks Dave, I looked at your code and it actually helped figuring out a different problem I hade with sprites where I would draw them always with an offset of self.x and self.y. Which meant they were drawn outside the screen all the time.
Also I figured out that setting the Size of a sprite with :setSize seems to be necessary to be able to draw them sometimes. I didn't really understand when it is need and when not.