Is there a convenient way to determine whether a sprite is in the display list (that is, add()
has been called, and remove()
hasn't been subsequently called) short of looping through the list of all sprites in attempt to find it?
The SDK provides a way to do this here.
playdate.graphics.sprite.getAllSprites()
Edit: I forgot to add that this returns an array. You could copy the list and then iterate through it to see if any of its values match something you want to find.
Sorry, I might not have been clear. I'm aware of how to do this by looping through the list returned by that function. I'm asking if that's the only way to do it, or if there's something more convenient, along the lines of mySprite.added
or mySprite:isDisplayed()
, etc.
playdate.graphics.sprite:isVisible()
?
Invisible sprites still remain in the display list, so that doesn’t provide the info I’m looking for.
Looks like we don't currently have a function that can tell you that; I've filed a feature request, will be easy to add. In the mean time you could override the current add/remove functions to add that ( untested code follows):
local spritelookup = {}
local _spriteadd = playdate.graphics.sprite.add
local _spriteremove = playdate.graphics.sprite.remove
playdate.graphics.sprite.add = function(s)
_spriteadd(s)
spritelookup[s] = true
end
playdate.graphics.sprite.remove = function(s)
_spriteremove(s)
spritelookup[s] = nil
end
playdate.graphics.sprite:wasAdded = function(s)
return spritelookup[s] ~= nil
end
I tested the code and now I appreciate that dynamite emoji above
It works well, but it creates a pseudo-memory leak: when sprites are removed by sprite.removeAll()
, this helper table keeps all the references to them and slowly fills the memory with non-discarded sprites. Unexpectedly to me, removeAll()
does not invoke the remove()
method on all sprites but just tosses the global draw list.
The solution is to make spritelookup
a weak table (Programming in Lua : 17), so that the references here don’t create an obstacle for GC to collect the discarded sprite.
In addition, I ran into some memory issues in Simulator (where GC is much faster than on a device) with this approach — the Sim would hard crash with segfault errors when lots of sprites that share data are first removed and then added back (eg when changing levels in a game).
I "fixed" it by making the table non-weak in the duration of the level transition. I’ll need to spend more time exploring this, because I’m not sure how that happens in a single-threaded environment. (I can create an example build and share it privately if it would be helpful.)
Can't you just override removeAll and clear the lookup table?
Something like:
local _spriteremoveall = playdate.graphics.sprite.removeAll
playdate.graphics.sprite.removeAll = function()
_spriteremoveall()
spritelookup = {}
end
That would be the solution for removeAll()
but I wasn’t sure if there isn’t another issue heading my way from somewhere else. But you’re probably right, it is a cleaner solution. (At least it doesn’t create a completely new baffling issue in Sim, haha.)