Sprite Layer
A sprite layer is a compositor surface that hosts pooled compositor sprites. SpriteLayer is the base surface layer. Hs2dSpriteLayer extends it with Hs2d sprite pools, logical sprite groups, culling, world-to-screen projection, and active sprite stats.
Gameplay sprites should use Hs2dSpriteLayer, Hs2dSpritePool, Hs2dSpriteGroup, tile layers, particle layers, or cached surfaces. Do not render actors, bullets, pickups, enemies, or map entities with raw per-frame screen draw loops.
When To Use
Use sprite layers for:
| Use | Recommended API |
|---|---|
| Actors, enemies, projectiles, pickups | layer.addSpritePool(...). |
| Tiled object entities with build-time spawn hooks | Hs2dWorldBuilder entities bindings. |
| Many logical objects over a fixed visible budget | layer.addSpriteGroup(...). |
| Dynamic tile sprites | world.addTileLayer({ mode: 'dynamic', layer }). |
| Particles | world.addParticleLayer({ layer }). |
| A modest number of actors where a full alpha surface costs more than direct blits | world.addDirectSpriteLayer(...) plus retained groups. |
Use plain SpriteLayer only for lower-level renderer work or diagnostics. New gameplay should default to Hs2dSpriteLayer.
Use Hs2dDirectSpriteLayer only when measurement favors direct cached-region blits. It retains slots and culls them like a renderer layer but has no backing surface. Hosanna Dungeon is the current production example.
Construction Pattern
Manual construction:
const foreground = world.addHs2dSpriteLayer({
id: 'foreground',
width: screenWidth,
height: screenHeight,
clearColor: 0x00000000,
cullPadding: 128,
minScale: 0.8,
maxScale: 1,
offscreenX: -1000,
offscreenY: -1000,
});
const projectilePool = foreground.addSpritePool({
id: 'projectiles',
capacity: 12,
definition: {
source: projectileAtlas,
frames: [{ id: 'bolt', x: 0, y: 0, width: 12, height: 42 }],
},
initialFrameId: 'bolt',
anchor: { x: 6, y: 21 },
zIndex: 108,
});
Tiled entity construction happens through Hs2dWorldBuilder.fromLevel. The builder creates the foreground layer on first need and creates Hs2dSpriteGroup instances for bound object types.
Update, Render, And Compose Behavior
| Phase | Behavior |
|---|---|
| Attach | Creates a transparent compositor surface and offset-aware compositor. |
| Scene sync | Game code writes logical state into sprite objects or layer sprite records. |
| Layer update | Marks the surface dirty, projects visible sprites through the camera, applies culling, and hides unused physical sprites. |
| Render | Draws the layer compositor into its surface when dirty. |
| Compose | Blits the sprite layer surface to the screen, optionally scaled by world zoom settings. |
projectionScaleRatio lets sprite projection use a different effective scale from the surface render scale. This is useful when world zoom should affect sprite positions but not all art size in the same way.
Performance Notes
- Allocate sprite pools during world build.
- Keep projectiles, enemies, pickups, and particles in fixed-capacity pools.
- Use
setVisible(false)and offscreen parking; do not remove and recreate sprites. - Cull world sprites through
Hs2dSpriteLayeror the visibility helper functions. - Use
compositorMarginXandcompositorMarginYwhen sprites can extend outside the visible viewport but still belong to the same layer. - Keep per-frame sync simple: set frame, world position, visible flag, and collision data.
Native Batch Paths
There are two related but distinct native batching APIs:
| API | Contract | Current sample use |
|---|---|---|
Hs2dNativeSpriteBatch | Standalone lower-level helper. Queue integer screen positions and regions, then flush() once at the intended z point. It owns its compositor sprites, calls DrawAll(), disables itself after allocation/draw failure, and requires a fallback. | No current sample uses it directly. |
Hs2dSpritePool.flushNativeBatch(...) | Opt-in fast projection/update path for an existing Hs2dSpritePool. Supply preculled parallel region-index and world-position arrays, set pool.isExternallyBatched = true, and let the normal layer compositor preserve z order. | Vertical Shooter uses it for coins, pickups, missiles, and wave enemies. |
The pool flush compacts visible entities into physical slots 0..count-1. Do not use it when gameplay depends on stable logical-index-to-native-handle identity. Vertical Shooter deliberately leaves its projectile collision pool on the ordinary path because collision handles are indexed by projectile slot.
Both paths are advanced optimizations. Keep their scratch arrays allocated and reused, cap counts by pool capacity, and retain an interpreted fallback where the standalone batch can fail.
Code Sample
protected override updateSceneSprites(): void {
const pool = this.projectileSpritePool;
if (!pool) return;
for (let i = 0; i < this.projectiles.items.length; i++) {
const projectile = this.projectiles.items[i];
const sprite = pool.getSprite(i);
if (!projectile.active) {
sprite.setVisible(false);
continue;
}
sprite.setWorldPosition(projectile.x, projectile.y);
sprite.setVisible(true);
sprite.setCollisionData({
kind: 'projectile',
entityId: projectile.collisionId,
ownerId: 'player',
damage: projectile.damage,
});
}
}
Source References
| Source | Why it matters |
|---|---|
../games/hosanna-ui/src/hosanna-game/scene/GameSceneRenderer.ts | SpriteLayer, compositor surfaces, offsets, dirty rendering, and composition. |
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dSpriteLayer.ts | Hs2dSpriteLayer, sprite groups, culling, projection, active counts, and visibility helpers. |
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dSprite.ts | Per-sprite frame, position, visibility, animation, anchor, and collision behavior. |
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dSpritePool.ts | Pool-level opt-in native flush, external batching flag, slot compaction, and normal pool API. |
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dDirectSpriteLayer.ts | Surface-less retained sprite groups and direct region composition. |
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dNativeSpriteBatch.ts | Low-level native compositor batching for measured special cases. |
../games/hosanna-ui/src/hosanna-game/hosanna2d/Hs2dWorld.ts | addHs2dSpriteLayer, sprite layer lookup, sprite pool registration, and world update. |
../hosanna-ui-game-samples-public/src/hosanna-game-examples/native-shoot-em-up/VerticalShooterLevelScene.ts | Practical projectile, ship, enemy group, collision, and render-visibility sync. |