More clarity on drawText and sprites

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

Did you try lockFocus(image)?

Once you do that all draw operations go to the image rather then the screen buffer. After you're done you call unlockFocus()

I think a minimal example on GitHub is the way to go.

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.

I don't see where you are adding the text?

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.

1 Like

I did not have time to look at your code, sorry.

BUT I added text to sprite to my progress bar demo.

See this commit: add text · gingerbeardman/progress-bar@eac4a71 · GitHub

9 lines of code:

  1. create image reference (1 loc)
  2. instantiate sprite using this image (4 loc)
  3. clear image, lock focus, draw text, unlock focus (4 loc)

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.

OK, first you should try to understand some things about my code.

I draw the bar in three parts:

  1. Background (white)
  2. Progress (dithered)
  3. Border/surround (transparent)
  • Background is white so we can see the difference between progress and not, and our game background will not show through.
  • Progress is dithered because it's a nice pattern.
  • The border/surround is mostly transparent so that we can see the progress bar through it.

Why did you paint the surround/border black? If you make it fully black you will see nothing of the progress bar!

Coupled with this, I am drawing the number as regular black text. You would need to use another drawing mode to draw text on a black background so it can be seen.

So, I'd be surprised if these issues are the same as in your code. I'll try to find time to look. Did you write all of your code yourself? Do you understand it all completely?

Finally, what about the original issue of drawing text on sprites. Can you do it now?

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

OK, to get text to appear on black background you should look at drawing modes

https://sdk.play.date/inside-playdate/#_drawing_mode

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 use draw modes in Circular and they are working fine.

playdate.graphics.setImageDrawMode(playdate.graphics.kDrawModeNXOR)

image

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)

1 Like

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

1 Like

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.

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

1 Like