Quick answer: Set sprite.dirty = 1 whenever you change its image or rect. For always-animating sprites use dirty = 2. The group needs clear() with a background each frame.

A UI built with LayeredDirty sprites for performance updates a health bar, but the screen doesn’t change. The dirty flag wasn’t set.

Dirty Flag Semantics

Set Dirty on Change

class HealthBar(pygame.sprite.DirtySprite):
    def set_health(self, hp):
        self.image = self._render(hp)
        self.dirty = 1   # mark for redraw

Any image or rect change must set dirty = 1, or LayeredDirty assumes the sprite is unchanged and skips it.

Group Setup

group = pygame.sprite.LayeredDirty()
group.clear(screen, background)   // supply the background to erase old positions

# each frame:
rects = group.draw(screen)
pygame.display.update(rects)   # only update changed rects

clear() with a background lets the group erase a sprite’s old position. draw() returns the changed rects; pass them to update() for partial-screen refresh.

Verifying

Health changes; bar redraws. Profiler / FPS counter shows the partial-update win — only changed regions blit each frame.

“LayeredDirty trades a dirty flag for performance. Forget the flag, forget the redraw.”

For mostly-static UI (HUD, menus), LayeredDirty is a real win. For a fully-animated scene, plain Group + full-screen flip is simpler.