More clarity on drawText and sprites

The problem : I'm trying to display some text, but it doesn't show up.

I've seen

and

I see Matts comment on drawing it in the sprite
https://devforum.play.date/t/text-is-hidden-invisible/5495/2

But all of it STILL isn't clicking for. After looking through so many post I'd like to make the post rule them all. I'll even go as far as making an example on github to link too, but I have to understand HOW to show text.

Problem context: I have many things on screen here:

Those two boxes are what I want to show text in..

How am I doing it well.... It's a bit involved but only because I broke it down.

I first made a generic progress bar by doing this.
progressBar.lua

function ProgressBar:init(x,y,width,widthRemaining)
    local progressBarBorderImage = gfx.image.new("images/progressBarBorder")
    local progressBarImage = gfx.image.new("images/progressBar")

    progressBarBorder = gfx.sprite.new()
    progressBarBorder:setImage(progressBarBorderImage)
    progressBarBorder.width = widthRemaining
    progressBarBorder.remainingFuel = width

    progressBarBorder:moveTo(x,y)
    progressBarBorder:add()

    self:setImage(progressBarImage)
    self.width = width - 4
    self.remainingFuel = widthRemaining - 4
    self:moveTo(x,y)
    self:add()
    
end

I then inherited this class in my fuelGauge class
fuelGauge.lua



local pd <const> = playdate
local gfx <const> = pd.graphics
import "ProgressBar"

class('FuelGauge').extends(gfx.sprite)
local progressBar = nil
function FuelGauge:init()
    FuelGauge.super.init(self)
    
    progressBar = ProgressBar(76,215,145,145)
    progressBar:add()
    
end

function FuelGauge:updateFuelGauge(newwidth)
    progressBar:setClipRect(progressBar.x-progressBar.width/2,progressBar.y-progressBar.height/2,newwidth,progressBar.height)
end

I then added this to my hudUI class
along with everything else on my hud
hudUi.lua


import 'CoreLibs/sprites'
import "coreLibs/object"
import "coreLibs/graphics"
import "FuelGauge"
import "HeatGauge"
import "Background"
import "TopOfScreen"

local pd <const> = playdate
local gfx <const> = pd.graphics

local backgroundImage = gfx.image.new("images/background")
local backgroundImage = gfx.image.new("images/background2")
local fuelGauge = nil
local heatGauge = nil
local background = nil
local tos = nil 

class('HudUi').extends(playdate.graphics.sprite)

function HudUi:updateFuelGauge(remainingFuel)
	fuelGauge:updateFuelGauge(remainingFuel)
end

function HudUi:updateHeatGauge(heat)
	heatGauge:updateHeatGauge(heat)
end

function HudUi:init()
	fuelGauge = FuelGauge()
	heatGauge = HeatGauge()
	background = Background()
	tos = TopOfScreen()
end

function HudUi:update()
	HudUi.super.update(self)
	self:collisions()
	
end

function HudUi:takeDamage()
	for ticks = 1, 4 do
		if ticks < 5 then
			playdate.display.setInverted(ticks % 2)
			playdate.wait(10)
		end
	end
end


function HudUi:collisions()
    local collisions = tos:overlappingSprites()
	
    if #collisions > 0 then
        for c = 1, #collisions do
			if collisions[c]:getTag() == 2 then -- rock
				collisions[c].isCrushed = true
				collisions[c]:remove()
			end
        end
    end
end

I then add hudUI.lua to my main.lua inialize()
main.lua

local function initalize()

	hudUi = HudUi(machine.isTakingDamage)
	hudUi:add()
end

Ok phew we're over that now ^....^

I'm wanting all of my hud stuff to stay in my hudUI class. But the text belongs to the gauges, like fuel gauge and heat gauge. so I'm assuming I'd add the drawtext to those sprites. but when I do I still get nothing...

I feel like I've done every trick I can think of and am hitting a wall. Could someone help me clarify what the heck is going on?

Still can't see it.... Maybe my zIndexes are off

It would be useful to show us how you tried to draw the text because none of the code you showed is handling text.

Usually I would also recommend to start with a very simple example at first to see how it would work before applying it to the full HUD.

Ack, no I didn't try the lockFocus. Lemme give it a try just for kicks

unlockFocus() didn't work... So I created the minimal demo that mimics my setup here. Maybe we can figure something out.
https://github.com/Mahekaru/PlaydateProgressBarWithText

I knew you where going to say that! I didn't add any of my previous tries as they didn't work.

I just pushed an update

@Nic , Ooops ok yes I can see that. I got flustered yesterday. I've had so many different forms of text I ended up removing it all to just start over and that is what we're seeing now. Let me try going back a few or at least to where I was trying out what @matt was talking about.

@matt Sorry for the confusion on this. To go along with @Nic reponse I'll update the GitHub to show what I had. Hopefully a working project can be created and shared to those who have a similar style.

-- UPDATE --
Ok done, i tried two different examples for bar1 and 2 adding comments as to what I feel is wrong.

Thanks @matt! It's a great example and I think I've found a clue as to what some of my problems are... One of which happens when I run you example.

If I open the progress-dither-table and make the bottom bar a solid black bar and save it it no longer shows O....O. I'm not using any special program to open it either, I just used paint.

Maybe I'm fighting some of the same kind of issues with my stuff. Again I didn't change anything but the bottom very bottom bar on your example.

Yes I wrote all of the code myself and understand most of it. I get yours is in three parts.

My initial reason for turning the bottom ditherd part black was to mimic what I have in my example. Yet after I did that it no longer showed.

I'm still not able to get the text to work, but it's blocks like this that hinder me. I'm thinking, why does just changing it to a full black bar break anything...

I'm still working with it, but I just wanted to post some of the odd progress I've made

I've used every draw mode there is. I assumed kDrawModeCopy or kDrawModeInverted is what I would want. Since it would be black text on white/transparent first, then should flip to white text on black... but doesn't happen.

Here are the three scenarios I'm encountering with the project.

When I open progress-dither-table-200-20.png and make the bottom dithered image fully black and save I get this.
For clarity, here is the code

import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"

local gfx <const> = playdate.graphics

local playerSprite = nil

local progressPercent = 0
local textOverlay = gfx.image.new(1,1)

playdate.display.setRefreshRate(20)

function myGameSetUp()
    local progressImage = gfx.imagetable.new("Images/progress-dither")
    assert( progressImage )

    infillSprite = gfx.sprite.new( progressImage[1] )
    infillSprite:moveTo( 200, 200 )
    infillSprite:add()
	
    progressSprite = gfx.sprite.new( progressImage[3] )
    progressSprite:moveTo( 200, 200 )
	updateProgress()
    progressSprite:add()

    surroundSprite = gfx.sprite.new( progressImage[2] )
    surroundSprite:moveTo( 200, 200 )
    surroundSprite:add()

    textOverlay = gfx.image.new(surroundSprite.width, surroundSprite.height)
    textSprite = gfx.sprite.new( textOverlay )
    textSprite:moveTo( 200, 200 )
    textSprite:add()
end

function updateProgress()
	progressSprite:setClipRect(progressSprite.x-progressSprite.width/2,progressSprite.y-progressSprite.height/2,progressPercent*2,progressSprite.height)

    textOverlay:clear(gfx.kColorClear)
    gfx.lockFocus(textOverlay)
    gfx.drawText(progressPercent.."%", 100,2)
    gfx.unlockFocus()
end

myGameSetUp()

function playdate.update()

	progressPercent = progressPercent + (math.random(0,4)//2)

	 if progressPercent > 120 then progressPercent = -20 end
	 updateProgress()
	 gfx.sprite.update()
	 playdate.timer.updateTimers()
end

With just the dithered bar turned black nothing shows.
Scene1

If I flip the order around in myGameSetup() so the images line up 1, 2, 3

import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"

local gfx <const> = playdate.graphics

local playerSprite = nil

local progressPercent = 0
local textOverlay = gfx.image.new(1,1)

playdate.display.setRefreshRate(20)

function myGameSetUp()
    local progressImage = gfx.imagetable.new("Images/progress-dither")
    assert( progressImage )

    infillSprite = gfx.sprite.new( progressImage[1] )
    infillSprite:moveTo( 200, 200 )
    infillSprite:add()
	
	surroundSprite = gfx.sprite.new( progressImage[2] )
    surroundSprite:moveTo( 200, 200 )
    surroundSprite:add()
	
    progressSprite = gfx.sprite.new( progressImage[3] )
    progressSprite:moveTo( 200, 200 )
	updateProgress()
    progressSprite:add()



    textOverlay = gfx.image.new(surroundSprite.width, surroundSprite.height)
    textSprite = gfx.sprite.new( textOverlay )
    textSprite:moveTo( 200, 200 )
    textSprite:add()
end

function updateProgress()
	progressSprite:setClipRect(progressSprite.x-progressSprite.width/2,progressSprite.y-progressSprite.height/2,progressPercent*2,progressSprite.height)

    textOverlay:clear(gfx.kColorClear)
    gfx.lockFocus(textOverlay)
    gfx.drawText(progressPercent.."%", 100,2)
    gfx.unlockFocus()
end

myGameSetUp()

function playdate.update()

	progressPercent = progressPercent + (math.random(0,4)//2)

	 if progressPercent > 120 then progressPercent = -20 end
	 updateProgress()
	 gfx.sprite.update()
	 playdate.timer.updateTimers()
end

The black bar is now visible, but the text blends into the bar when it scrolls over it.
scene2

I've tried every drawmode with very little success. To prove to myself it does something I added inverted knowing that it would only draw in one, but again just wanted to show myself something was happening.

import "CoreLibs/object"
import "CoreLibs/graphics"
import "CoreLibs/sprites"
import "CoreLibs/timer"

local gfx <const> = playdate.graphics

local playerSprite = nil

local progressPercent = 0
local textOverlay = gfx.image.new(1,1)

playdate.display.setRefreshRate(20)

function myGameSetUp()
    local progressImage = gfx.imagetable.new("Images/progress-dither")
    assert( progressImage )

    infillSprite = gfx.sprite.new( progressImage[1] )
    infillSprite:moveTo( 200, 200 )
    infillSprite:add()
	
	surroundSprite = gfx.sprite.new( progressImage[2] )
    surroundSprite:moveTo( 200, 200 )
    surroundSprite:add()

    progressSprite = gfx.sprite.new( progressImage[3] )
    progressSprite:moveTo( 200, 200 )
	updateProgress()
    progressSprite:add()

    textOverlay = gfx.image.new(surroundSprite.width, surroundSprite.height)
    textSprite = gfx.sprite.new( textOverlay )
    textSprite:moveTo( 200, 200 )
    textSprite:add()
end

function updateProgress()
	progressSprite:setClipRect(progressSprite.x-progressSprite.width/2,progressSprite.y-progressSprite.height/2,progressPercent*2,progressSprite.height)
	playdate.graphics.setImageDrawMode(playdate.graphics.kDrawModeInverted)
    textOverlay:clear(gfx.kColorClear)
    gfx.lockFocus(textOverlay)
    gfx.drawText(progressPercent.."%", 100,2)
    gfx.unlockFocus()
end

myGameSetUp()

function playdate.update()

	progressPercent = progressPercent + (math.random(0,4)//2)

	 if progressPercent > 120 then progressPercent = -20 end
	 updateProgress()
	 gfx.sprite.update()
	 playdate.timer.updateTimers()
end

And it shows, just like I thought it would. To me this feels like something isn't right.
kDrawModeCopy or kDrawModeXOR or kDrawModeNXOR
should all work, but don't.

scene3

I’m thinking kDrawModeNXOR will draw white on black if the black is from the current focus/context but not if the black progress bar is a different sprite. I don’t have a computer with me to check, but try it as a single sprite, with the outline & progress bar then text drawn in its update.

Edit: or maybe setting the sprite’s draw mode?

textSprite:setImageDrawMode(gfx.kDrawModeNXOR)

Whelp I was able to get it to work by doing it this way.

Main.lua

import "coreLibs/object"
import "coreLibs/graphics"
import "coreLibs/sprites"
import "coreLibs/timer"

import "ProgressBar"

local pd <const> = playdate
local gfx <const> = pd.graphics

local progressBar = nil

local function initalize()
	progressBar = ProgressBar(100,25,145,145)
	progressBar:add()
end

initalize()

local progress1 = 0
local progress2 = 141
function playdate.update()
	gfx.sprite.update()
	pd.timer.updateTimers()
	progressBar:drawText()

	if pd.buttonIsPressed(pd.kButtonA) then
		progress1 +=1
		progressBar:increaseBar(progress1)
	end

	if pd.buttonIsPressed(pd.kButtonB) then
		progress1 -=1
		progressBar:decreaseBar(progress1)
	end
end

progressBar.lua

import "CoreLibs/math"
local pd <const> = playdate
local gfx <const> = pd.graphics
local Sprite = gfx.sprite
local progressBarText = ""
local white = false
local black = false

class('ProgressBar').extends(Sprite)

function ProgressBar:init(x,y,width)
    local progressBarBorderImage = gfx.image.new("images/progressBarBorder")
    local progressBarImage = gfx.image.new("images/progressBar")

    local progressBarBorder = gfx.sprite.new()
    progressBarBorder:setImage(progressBarBorderImage)
    progressBarBorder.width = width
 
    progressBarBorder:moveTo(x,y)
    progressBarBorder:add()

    textSprite = gfx.sprite.new( textOverlay )
    textSprite:moveTo( 200, 200 )
    textSprite:add()
    
    self:setImage(progressBarImage)
    self.width = width - 4
    self:moveTo(x,y)
    self:add()

    self:increaseBar(0)
end

function ProgressBar:increaseBar(newwidth)
    progressBarText = newwidth
    self:setClipRect(self.x-self.width/2,self.y-self.height/2,newwidth,self.height)
end

function ProgressBar:decreaseBar(newwidth)
    progressBarText = newwidth
    self:setClipRect(self.x-self.width/2,self.y-self.height/2,newwidth,self.height)
end

function ProgressBar:drawText()
    local percentage = math.floor((progressBarText / self.width) * 100)

    if percentage >= 50 and white == false then
        playdate.graphics.setImageDrawMode("fillWhite")
        white = true
        black = false
    end

    if percentage < 50 and black == false then
        playdate.graphics.setImageDrawMode("fillBlack")
        white = false
        black = true
    end

    gfx.drawText(percentage.."%", 100,17)

end


progress

Update

  • Only seems to work with one through. I put two on screen then the text will only show for the last one added.

Honestly I'm over getting this to work. I've spent I don't know how many hours trying to figure out what the heck is wrong. I'm ready to just bake the text in.

This is a bit simpler:

local pd <const> = playdate
local gfx <const> = pd.graphics

class('ProgressBar').extends(gfx.sprite)

function ProgressBar:init(x, y, width, height)
	self:setImage(gfx.image.new(width, height))
	self.progress = 0
	self:moveTo(x, y)
	self:add()
	self:set(0)
end

function ProgressBar:set(percentage)
	self.progress = percentage
	local bar_image = gfx.image.new(self.width, self.height, gfx.kColorWhite)
	local progressWidth = self.progress/100 * (self.width - 4)
    local _, fontHeight = playdate.graphics.getTextSize("TEST")
	gfx.pushContext(bar_image)
	gfx.setLineWidth(2)
	gfx.drawRoundRect(1, 1, self.width-2, self.height-2, 3)
	gfx.fillRect(2, 2, progressWidth, self.height - 4)
	gfx.setImageDrawMode(gfx.kDrawModeNXOR)
	gfx.drawTextAligned(math.floor(self.progress) .. "%", self.width/2, (self.height - fontHeight)/2 + 2, kTextAlignment.center)
	gfx.popContext()
	self:setImage(bar_image)
end

There's a bit of a gotcha in local bar_image = gfx.image.new(self.width, self.height, gfx.kColorWhite). If the last parameter isn't set, the background will be transparent, so NXOR doesn't draw anything because there isn't an opposite colour.

ProgressBar

This looks so good!

I tried doing something like this a while back. @matt helped me through it, and I ended up going down the route of using images instead.
https://devforum.play.date/t/how-to-create-a-progress-bar/6321/5

@TheMediocritist, this solution is great thank you so much!