[C-API] Stopping Sprite Updates

C-API / Linux / SDK 1.12.3

I am getting a crash on playdate->sprite->setUpdateFunction( sprite, NULL );.
I guess it's because I'm null~ing the update function pointer while some kind of update-loop is in progress, and the function pointer not being checked right before calling.

Is there an appropriate time to call this function?
I guess the answer is "outside of the playdate->sprite->playdate->sprite->updateAndDrawSprites(); function"... I'll go check that now.

EDIT: Yes, call it outside the updateAndDrawSprites() loop and you're OK.

And I just noticed that the sprite's update function is only called when the sprite is on-screen. So removing the update function pointer to stop this is pointless. (Another item for the doco).

1 Like

Didn’t know that it was possible to set NULL. I usually set the update function to an empty function instead :sweat_smile:.

Hm, I'm not seeing that. This is printing sprite->update() every cycle for me:

PlaydateAPI* pd;

int update(void* userdata)
{
	pd->sprite->updateAndDrawSprites();
	return 1;
}

void updatesprite(LCDSprite* s)
{
	pd->system->logToConsole("sprite->update()");
}

#ifdef _WINDLL
__declspec(dllexport)
#endif
int eventHandler(PlaydateAPI* playdate, PDSystemEvent event, uint32_t arg)
{
	if ( event == kEventInit )
	{
		pd = playdate;
		pd->system->setUpdateCallback(update, NULL);
		
		LCDSprite* s = pd->sprite->newSprite();
		pd->sprite->setUpdateFunction(s, updatesprite);
		pd->sprite->moveTo(s, -100,-100);
		pd->sprite->addSprite(s);
	}
	
	return 0;
}

You're exactly right about the crash, though--so that we don't have a sprite changing the display list at the same time we're walking it, we first make a list of sprites that have the updateEnabled flag set and have an update function and then go through the list and call those sprites' update functions. We need to double-check that the function hasn't been nulled before we call it.

Ok, so I'm not seeing the continuous updates. When I start a bullet sprite, I see its bulletUpdate() being called until it goes away. But in my recycling function, it is only calling playdate->sprite->removeSprite( bullet );

Is removeSprite() clearing this updateEnabled flag? I can't seem to find details on this in the doco, so I assume it's internal.

When I pull a new (recycled) bullet from the pool, the only sprite call made on it is playdate->sprite->addSprite( bullet ). So the sprite add/remove looks like the culprit.

But can you please confirm this?

Can't comment directly on this (I stopped using the setUpdateFunction to get more control), but it doesn't sound too unreasonable that you'd need to set it again when re-adding the sprite to the display list the 2nd+ times.

Just wanted to say that your overall approach is probably a good way to be going here. I can get upwards of 4k spirits in my game, but having them all in the display list at once tanks performance. I only keep around 100 or so in the display list at any one time (culled on the camera, updated periodically).

You might find for a bullet hell or similar that re-populating the display list every second (or so) to remove off screen bullets (they can still live on in your pool) will similarly work well.

1 Like

Oh, I typoed there: I should have said updatesEnabled (plural, see pd->sprite->setUpdatesEnabled()), not updateEnabled. Still no luck reproducing the bug, though. :confused: I changed the test above to

int count = 0;

int update(void* userdata)
{
	++count;
	
	if ( count == 10 )
		pd->sprite->removeSprite(s);
	else if ( count == 20 )
		pd->sprite->addSprite(s);

	pd->sprite->updateAndDrawSprites();
	return 1;
}

void updatesprite(LCDSprite* s)
{
	pd->system->logToConsole("sprite->update() %i", count);
}

and it prints from 1 to 9, then 20 and up.