139 lines
3.8 KiB
Markdown
139 lines
3.8 KiB
Markdown
# Scene System
|
|
|
|
Source: `src/dusk/scene/`
|
|
|
|
## Overview
|
|
|
|
The scene system is the top-level coordinator for a running game state.
|
|
It manages one active scene at a time. Scenes are JS scripts -- each
|
|
scene is a `.js` asset file that exports an object with lifecycle hooks.
|
|
The scene system loads, ticks, and tears down these scripts, while the
|
|
C side runs the ECS and render pipeline on each tick.
|
|
|
|
## Scene lifecycle (C side)
|
|
|
|
```c
|
|
extern scene_t SCENE;
|
|
|
|
errorret_t sceneInit(void); // initialise the scene manager
|
|
errorret_t sceneUpdate(void); // process pending transition, tick active scene
|
|
errorret_t sceneRender(void); // render entities + render pipeline + UI
|
|
errorret_t sceneDispose(void); // dispose the active scene immediately
|
|
```
|
|
|
|
`sceneUpdate` each tick:
|
|
1. Checks for a pending scene transition and performs it (dispose old,
|
|
load and init new).
|
|
2. Calls the JS scene's `update()` hook.
|
|
3. Calls `entityManagerUpdate()` to fire all entity update callbacks.
|
|
|
|
`sceneRender` each tick:
|
|
1. Binds the screen.
|
|
2. Calls `sceneRenderPipeline()` -- renders all entities with a
|
|
`COMPONENT_TYPE_RENDERABLE` in priority order.
|
|
3. Renders UI.
|
|
4. Calls the JS scene's `render()` hook (for any custom drawing).
|
|
5. Unbinds the screen.
|
|
|
|
## Scene lifecycle (JS side)
|
|
|
|
A scene file exports a plain object with these optional hooks:
|
|
|
|
```js
|
|
var scene = {};
|
|
|
|
scene.init = async function() {
|
|
// Load assets, create entities, set up state.
|
|
// May be async -- await asset loads here.
|
|
};
|
|
|
|
scene.update = function() {
|
|
// Called each fixed-timestep tick.
|
|
};
|
|
|
|
scene.render = function() {
|
|
// Called each render tick, after ECS renderables.
|
|
};
|
|
|
|
scene.dispose = function() {
|
|
// Clean up entities and state.
|
|
};
|
|
|
|
module.exports = scene;
|
|
```
|
|
|
|
See `CLAUDE.md` -- "JavaScript (asset scripts)" for JS style rules.
|
|
|
|
## Render pipeline (`scenerenderpipeline.h`)
|
|
|
|
`sceneRenderPipeline(cameraEntityId)` gathers all active
|
|
`COMPONENT_TYPE_RENDERABLE` components, sorts them by effective
|
|
priority, and draws each one using its shader.
|
|
|
|
**Priority rules:**
|
|
- `renderable.priority != 0` -- use that value directly.
|
|
- `renderable.priority == 0` -- auto-derive: opaque geometry sorts
|
|
before transparent geometry; sprite batches sort before shader
|
|
materials; etc.
|
|
- Lower priority number = drawn first (behind); higher = drawn last
|
|
(on top).
|
|
|
|
The shader used for each renderable:
|
|
- `ENTITY_RENDERABLE_TYPE_SPRITEBATCH` and `CUSTOM` default to
|
|
`SHADER_LIST_SHADER_UNLIT`.
|
|
- `ENTITY_RENDERABLE_TYPE_SHADER_MATERIAL` uses the shader indexed by
|
|
`renderable.data.material.shaderType` in `SHADER_LIST_DEFS`.
|
|
|
|
## Transitioning between scenes
|
|
|
|
Scene transitions are handled entirely in JS via the `Scene` global.
|
|
The `Scene` object is a singleton with:
|
|
|
|
```js
|
|
// Switch to a new scene. Calls dispose() on the current scene, then
|
|
// init() on the new one. Both happen synchronously this tick.
|
|
Scene.set(newSceneObject);
|
|
|
|
// The current scene object (may be null):
|
|
Scene.current
|
|
```
|
|
|
|
Typical scene-switch pattern:
|
|
|
|
```js
|
|
// Inside a scene's update or event handler:
|
|
const nextScene = require("scenes/gameplay.js");
|
|
Scene.set(nextScene);
|
|
```
|
|
|
|
`Scene.set` is synchronous -- it calls `dispose` on the old scene and
|
|
`init` on the new scene before returning. If `init` needs async work
|
|
(loading assets), use an async function and `await` inside `init`:
|
|
|
|
```js
|
|
nextScene.init = async function() {
|
|
await batch.load(); // wait for assets before proceeding
|
|
};
|
|
```
|
|
|
|
The C side does not defer the transition; the switch happens inside
|
|
the current `sceneUpdate` call.
|
|
|
|
## Relationship to the engine loop
|
|
|
|
```
|
|
engineUpdate()
|
|
timeUpdate()
|
|
inputUpdate()
|
|
physicsManagerUpdate()
|
|
scriptUpdate() <- runs JS microjobs
|
|
sceneUpdate() <- JS update + ECS entity updates
|
|
|
|
engineUpdate() -> sceneRender()
|
|
screenBind()
|
|
sceneRenderPipeline() <- ECS renderables sorted by priority
|
|
uiRender()
|
|
sceneRender (JS hook)
|
|
screenUnbind / screenRender
|
|
```
|