Scene2D
A 2D world: a draw set plus an orthographic Camera2D. Opening a scene brings up
the 2D engine on the canvas and starts rendering; only one scene is active at a time. Not to be
confused with Scene (the 3D world).
At a glance
const scene = new Scene2D({ background: '#10131a' })
scene.layer(1).ySort() // layer 1 sorts top-down by Y
const tex = await Texture2D.load(asset('./hero.png'))
const hero = new Sprite({ texture: tex, anchor: [0.5, 1], layer: 1 })
scene.add(hero)
scene.addEventListener('click', ev => {
console.log('tapped', ev.target?.id ?? 'empty space', 'at world', ev.worldX, ev.worldY)
})
scene.open()Creating
new Scene2D(options?: {
background?: ColorInput // clear color
filter?: 'nearest' | 'linear' // texture sampling; default nearest (crisp pixel art)
})
scene.background = '#10131a' // settable later too
scene.camera // the scene's Camera2D (readonly)filter is a global renderer setting — one sampler for all textures, not
per-texture. It is (re)applied on open(), so the currently-open scene's choice wins. Use
'linear' for hi-res / non-pixel-art assets; omit to leave the current setting.
Opening & closing
scene.open(): this // make active: brings up the 2D engine, closes any other scene
scene.close(): this // stop rendering this scene
scene.destroy(): void // close + free the native scene
Scene2D.active // static: the currently open Scene2D, or nullNodes
scene.add(...nodes: Node2D[]): this
scene.remove(...nodes: Node2D[]): thisAdds/removes root-level Node2Ds. Child nodes come along with their parent — only
roots need scene.add.
Layers & Y-sort
scene.layer(n).ySort(enabled = true): LayerHandleNodes pick their layer with node.layer (higher layers draw on top; within a layer, node.z
orders). Calling .ySort() on a layer switches it to top-down depth sorting: sprites lower on
screen overlap sprites higher up — the classic "characters walk behind trees" look.
Y-sort compares node positions, i.e. anchor points. Give characters and props on a
Y-sorted layer a bottom-center anchor (anchor: [0.5, 1] — the "feet") so depth sorts where
they touch the ground.
Picking
scene.pick(worldPoint: Vec2Like): Node2D | nullTopmost visible sprite whose world bounds contain the point — a body-free hit-test, no physics
needed. Pair with scene.camera.screenToWorld() to convert a tap position.
For automatic pointer dispatch to nodes, give the node a physics body instead — see picking in 2D physics.
Scene-level pointer events
scene.addEventListener('click', (ev: ClickEvent) => void) // pointer-up anywhere in the scene
scene.addEventListener('touchstart', (ev: TouchStartEvent) => void) // pointer-down; ev.track() for drags
scene.removeEventListener(channel, callback)The handler fires for every tap/press in the scene; ev.target is the hit physics-body node
or null (empty space). ev.worldX / ev.worldY give the hit point in world space. On
'touchstart' call ev.track({...}) to capture the drag — see
pointer events & gestures.
See also
- Node2D — transform, hierarchy, layer/z, events
- Camera2D — zoom, screen↔world conversion
- Sprite — textured nodes, anchors
- 2D physics — what makes a node pointer-hittable
- Pointer events & gestures —
ClickEvent,ev.track()