Moving a Background Image and/or Displaying Sprites Above Images

Hi, I'm a new dev trying to create a background image that I can move using the crank. Currently, I have the mechanic working, except for the fact that in its current state (not a background image) I can't display sprites above the image. I've tried using setZIndex on both my test sprite and my image, but it still doesn't seem to work.

Due to the way I've implemented moving the image up and down with the crank (creates a scrolling effect) I'm adjusting the y position of the image every time there's a change in the crank position, and at each draw, I'm using drawAnchored because I want the default position to show the bottom of the image (not the top).

Could I get some guidance on what the best course of action would be here? In essence, I just need the image as far back as possible and have the ability to scroll/move it on the y-axis, while being able to display sprites above it. Thanks!

main.lua:

--main imports
import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"

--declarations
local gfx <const> = playdate.graphics
local baseY = 240

--setup func
local function setup()
	
	--board image
	backgroundImage = gfx.image.new("images/background")
	assert( backgroundImage )

	--star test
	local testStar = gfx.image.new("images/testStar")
	testStarDisplay = gfx.sprite.new(testStar)
	
end

function scrollBoard()
	
	local change, acceleratedChange = playdate.getCrankChange()
	baseY += change

	if acceleratedChange > 15 then
		acceleratedChange = 15
	end

	if acceleratedChange < -15 then
		acceleratedChange = -15
	end

	baseY += acceleratedChange
	
	if baseY <= 400 then
		if baseY >= 240 then
			backgroundImage:drawAnchored(0, baseY, 0.0, 1.0)
			
		else
			baseY = 240
			backgroundImage:drawAnchored(0, 240, 0.0, 1.0)
	
		end
	else
		baseY = 400
		backgroundImage:drawAnchored(0, 400, 0.0, 1.0)
		
	end

end	

--calling setup functions
setup()

--main update func
function playdate.update()

	gfx.sprite.update()
	
	scrollBoard()

	if playdate.buttonIsPressed(playdate.kButtonA) then
		print("pressed")
		testStarDisplay:moveTo(200, 120)
		print("after move")
		testStarDisplay:add()
		print("after add")
		testStarDisplay:setZIndex(32767)
		print("after zindex")
	end

	playdate.drawFPS(0, 0)
end

You should just be able to call gfx.sprite.update() after scrollBoard() instead of before. When you draw an image directly (not in a sprite), it is not influenced by ZIndex or any other factors other than the order the draw functions are called.

Weirdly enough, when I do that, the simulator shows that the screen is updating, but the image isn't visible. The test sprite does show up though.

Yeah, it seems like gfx.sprite.update() clears the screen when it's called, so I guess it isn't possible to draw images behind sprites without making the background itself a sprite. I reimplemented the background as a sprite, and it seems to work correctly. I wasn't really sure what you meant by the default position showing the bottom of the image, so I just used the top left as the origin, but you could change the center to setCenter(0, 1) if you want.

--main imports
import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"

--declarations
local gfx <const> = playdate.graphics
local baseY = 0

--setup func
local function setup()
	
	--board image
	backgroundImage = gfx.image.new("images/background")
	backgroundSprite = gfx.sprite.new(backgroundImage)
	backgroundSprite:setZIndex(-32768)
	backgroundSprite:setCenter(0, 0)
	backgroundSprite:add()
	assert( backgroundImage )

	--star test
	local testStar = gfx.image.new("images/testStar")
	testStarDisplay = gfx.sprite.new(testStar)
	
end

function scrollBoard()
	
	local change, acceleratedChange = playdate.getCrankChange()
	baseY += change

	if acceleratedChange > 15 then
		acceleratedChange = 15
	end

	if acceleratedChange < -15 then
		acceleratedChange = -15
	end

	baseY += acceleratedChange
	
	if baseY < 0 then baseY = 0 end
	backgroundSprite:moveTo(0, baseY)

end	

--calling setup functions
setup()

--main update func
function playdate.update()

	gfx.sprite.update()
	
	scrollBoard()

	if playdate.buttonIsPressed(playdate.kButtonA) then
		print("pressed")
		testStarDisplay:moveTo(200, 120)
		print("after move")
		testStarDisplay:add()
		print("after add")
		testStarDisplay:setZIndex(32767)
		print("after zindex")
	end

	playdate.drawFPS(0, 0)
end
1 Like

It works!! Thank you so much!!

I had to edit the scrollboard func a little from what you changed as it wasn't working completely(I also didn't fully understand what you wrote) but everything works! I also essentially replicated drawAnchored(0, 240, 0.0, 1.0) by using setCenter(0, 1) (thank you for that) and moveTo(0, 240).

For anyone who might have the same issue in the future this is the full edited code:

--main imports
import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"

--declarations
local gfx <const> = playdate.graphics
local baseY = 0

--setup func
local function setup()
	
	--board sprite
	backgroundImage = gfx.image.new("images/background")
	backgroundSprite = gfx.sprite.new(backgroundImage)
	backgroundSprite:setZIndex(-32768)
	backgroundSprite:setCenter(0, 1)
	backgroundSprite:moveTo(0, 240)
	backgroundSprite:add()
	assert( backgroundImage )

	--star test
	local testStar = gfx.image.new("images/testStar")
	testStarDisplay = gfx.sprite.new(testStar)
	
end

--scrolling along using the crank
function scrollBoard()

	--get change
	local change, acceleratedChange = playdate.getCrankChange()

	--adding the change in crank value to baseY
	baseY += change

	--making sure you can't crank too quickly and produce a glithc effect
	if acceleratedChange > 10 then
		acceleratedChange = 10
	end

	if acceleratedChange < -10 then
		acceleratedChange = -10
	end

	--adding the acceleratedChange to the total baseY var
	baseY += acceleratedChange
	
	--scrolling & adding a limit to how far you can scroll
	if baseY <= 400 then
		if baseY >= 240 then
			backgroundSprite:moveTo(0, baseY)
		else
			baseY = 240
			backgroundSprite:moveTo(0, baseY)
		end
	else
		baseY = 400
		backgroundSprite:moveTo(0, baseY)
	end
end	

--calling setup function(s)
setup()

--main display update func
function playdate.update()

	gfx.sprite.update()
	scrollBoard()
	--button star sprite test
	if playdate.buttonIsPressed(playdate.kButtonA) then
		testStarDisplay:moveTo(200, 120)
		testStarDisplay:add()
		testStarDisplay:setZIndex(32767)
	end

	playdate.drawFPS(0, 0)
end

I found I had to make my background tile map a sprite aswell to have it show up below the player. It seems that the single file tilemap example maybe out of date or something as I cannot for the life of me work out how they display the map without adding it to a sprite but it still works ?!

I’m Currently stuck trying to implement Addwallsprites to the right tiles :crazy_face:

If any of this code is useful however take a look