I repeatedly have this issue and sometimes I manage to make it work but I feel I am not understanding what is going on when writing text on a sprite.
In a nutshell, I can make text appear just fine if I create an image from scratch but I cannot write on an existing image, is it by design?
I have a simple class showing the name of a building on a sign. This name is updated whenever required by the main loop. The Sign "frame" is displayed when required but the text itself is not. Would anyone understand why?
class("BuildingSign").extends(gfx.sprite)
function BuildingSign:init()
BuildingSign.super.init(self)
self:moveTo(106, 145)
self:setVisible(false)
self:add()
end
function BuildingSign:updateText(cellType)
local imgSign = gfx.image.new("images/buildingSign") -- This one doesn't work
-- local imgSign = gfx.image.new(70, 20, gfx.kColorWhite) -- This one works
assert(imgSign)
self:setImage(imgSign)
if (cellType >= 3) then -- building type
local buildingName = ""
if (cellType == 3) then buildingName = "POLICE"
elseif (cellType == 4) then buildingName = "BANK"
elseif (cellType == 5) then buildingName = "GARAGE"
elseif (cellType == 6) then buildingName = "FUEL"
end
gfx.pushContext(imgSign)
gfx.setColor(gfx.kColorXOR)
gfx.drawTextAligned(buildingName, imgSign.width / 2, 0, kTextAlignment.center)
gfx.popContext()
self:setVisible(true)
else
self:setVisible(false)
end
end```
For this passing by, my mistake is that gfx.setColor is not applied to font/text but to primitives only (not bitmap). It seems we can use the setImageDrawMode instead.
Please, please, correct me if I am wrong on this!
Thanks for looking into this. My mistake was to forget that texts are image, so the setColor has no effect there. We have to set the drawmode instead. It IS in the doc, but I somehow missed / not read this part...
The text was written but in black, on a black background... Classic!
Neat. So does sprite:setImage just pass the sprite a reference to the original image? So I can draw to the original instead of doing sprite:getImage, drawing to that and then doing another sprite:setImage?
So I am not sure that a reference (as in a pointer is actually passed as I am not sure how it works in Lua), nonetheless there is no need to create multiple images indeed. I pasted the code I am using below and will probably change it further to update a text variable, mark the sprite dirty and redefine draw if needed
Nota: still testing so there may be some bugs
local pd <const> = playdate
local gfx <const> = pd.graphics
class("BuildingSign").extends(gfx.sprite)
function BuildingSign:init()
BuildingSign.super.init(self)
self:moveTo(106, 145)
self:setVisible(false)
self:add()
end
function BuildingSign:updateText(cellType)
if (cellType >= 3) then -- building type
local buildingName = ""
if (cellType == 3) then buildingName = "POLICE"
elseif (cellType == 4) then buildingName = "BANK"
elseif (cellType == 5) then buildingName = "GARAGE"
elseif (cellType == 6) then buildingName = "FUEL"
end
local imgSign = gfx.image.new("images/buildingSign")
assert(imgSign)
self:setImage(imgSign)
gfx.pushContext(imgSign)
local fnt = gfx.font.new("fonts/Blades of Steel")
gfx.setFont(fnt)
gfx.setImageDrawMode(gfx.kDrawModeNXOR)
gfx.drawTextAligned(buildingName, self.width / 2, 8, kTextAlignment.center)
gfx.popContext()
self:setVisible(true)
else
self:setVisible(false)
end
end
EDIT: I made further tests and as I thought if you are moving from one text to another the previously submitted code will merge the texts as well, which is not the expected behavior. In this instance, we have no choice but to reload the original image to "start fresh" - I updated the code accordingly
I wonder if there is a smarter / more efficient way to do this
Loading from disk is expensive. I'd recommend doing this once only in your init.
To achieve your goal create a copy of your loaded image and draw into that, then when you need to reset it do so with the other already loaded version.
Thanks @matt for the great tip, I didn't know. I haven't paid attention to the optimisation yet but might as well adopt best practices now!
Is there any doc online talking about the optimisation besides just trying to go easy on the CPU?
EDIT: Below is the modified code with the copy for those interested.
local pd <const> = playdate
local gfx <const> = pd.graphics
class("BuildingSign").extends(gfx.sprite)
function BuildingSign:init()
BuildingSign.super.init(self)
-- We record the original copy for later use
self.imgOriginalSign = gfx.image.new("images/buildingSign")
assert(self.imgOriginalSign)
self.fntSign = gfx.font.new("fonts/Blades of Steel")
assert(self.fntSign)
self:setSize(self.imgOriginalSign:getSize())
self:moveTo(106, 145)
self:setVisible(false)
self:add()
end
function BuildingSign:updateText(cellType)
if (cellType >= 3) then -- building type
local buildingName = ""
if (cellType == 3) then buildingName = "POLICE"
elseif (cellType == 4) then buildingName = "BANK"
elseif (cellType == 5) then buildingName = "GARAGE"
elseif (cellType == 6) then buildingName = "FUEL"
end
-- We copy the original Image as disk access is expensive
local imgSign = self.imgOriginalSign:copy()
gfx.pushContext(imgSign)
gfx.setFont(self.fntSign)
gfx.setImageDrawMode(gfx.kDrawModeNXOR)
gfx.drawTextAligned(buildingName, self.width / 2, 8, kTextAlignment.center)
gfx.popContext()
self:setImage(imgSign)
self:setVisible(true)
else
self:setVisible(false)
end
end