Scenes
Volver a la referencia

Scenes

Declaring scenes, switching between them, pausing one without stopping the loop, the transitions, and the scene document.

48 símbolos

createScenefunctionnacatamalon

createScene(children: typeOperator) => TScene

Ends a scene's body, and is what it returns: return createScene().

Everything the body made (sprites, models, objects from useSpawn) is already in the scene, so it is usually called with nothing. children is for objects built somewhere else and handed in. It touches nothing itself: it only describes.

It takes no name. A scene is named by its key in what createGame is given, so the game knows every scene by name as soon as it starts, before any of them has run.

Parámetros

childrentypeOperator

Objects made outside this body that should hang from the scene too.

Ejemplo

const Menu: TSceneFn = () => createScene();

const Level: TSceneFn = () => {
    const hero = createSprite({ width: 16, height: 16 });
    useUpdate((delta) => {
        hero.transform.x += 30 * delta;
    });
    return createScene();
};

createGame('#app', { width: 320, height: 240 })({ Menu, Level });
Ver el código · scene/create_scene.ts:36

fadefunctionnacatamalon

fade(duration: number, color: TColor) => TTransition

The screen sinks into one colour, the scenes swap behind it, and it comes back.

The one every game reaches for, and the one to reach for when nothing else fits: it says "we went somewhere else" without saying anything about where, so it never looks wrong.

Note that a fade runs before the rest of the chain, so on a game that reduces its colours the middle of it is quantized like everything else: stepped, not smooth. That is how a fade looked on the hardware this engine is aimed at, and it is the reason the transition goes first.

Parámetros

durationnumber

How long the whole thing takes in milliseconds, covering and uncovering together.

colorTColor

What the screen sinks into. Black unless you say otherwise.

Ver el código · transition/builtin/fade.ts:25

irisfunctionnacatamalon

iris(duration: number, color: TColor, centre: { x, y }) => TTransition

A circle closes over the screen and opens again on the scene behind it.

The centre is a parameter, and that is the whole point of the effect. An iris that can only close on the middle of the screen is a decoration; one that closes on where the character is standing is the shot that ends a level, and it is what this has been used for since it was done with a piece of card in front of a lens.

The circle is corrected for the shape of the window, so it stays a circle on a wide screen instead of becoming an ellipse, and it opens far enough past the furthest corner that "open" really means nothing covered, wherever the centre is.

Parámetros

durationnumber

How long the whole thing takes in milliseconds, closing and opening together.

colorTColor

What the outside of the circle is painted. Black unless you say otherwise.

centre{ x, y }

Where it closes on, in screen fractions: { x: 0, y: 0 } is the top left corner and { x: 1, y: 1 } the bottom right. The middle unless you say otherwise.

Ver el código · transition/builtin/iris.ts:30

parseSceneDocfunctionnacatamalon

parseSceneDoc(value: unknown, src: string) => TSceneDoc

Reads a scene document, whatever state it is in.

Total and never throws. A field that is missing, of the wrong shape or plain nonsense falls back to its default and the scene still opens. A scene is written by tools and edited by hand, so treating it as a promise would mean one typo costs you the level.

What it does say out loud, once per document rather than once per field:

  • a file that is not a scene of this engine, which comes back empty, because carrying on would mean quietly presenting somebody else's file as an empty level;
  • one written by a newer version, which is read anyway: most of it is still readable, and what is not is kept rather than lost;
  • components and assets this version does not know, which are counted and named.

Nothing is deleted on the way in. An unknown component is kept beside its box and an unknown field is kept on it, so a document opened here and written back out again is the document that came in.

Parámetros

valueunknown

Whatever JSON.parse gave back.

srcstring

Where it came from, named in every warning. A game opens a dozen scenes and they all fail the same way; a message that does not say which one costs you the afternoon.

Ver el código · scene/document/parse_scene_doc.ts:835

pixelatefunctionnacatamalon

pixelate(duration: number, color: TColor, minBlocks: number) => TTransition

The picture falls apart into bigger and bigger blocks, then into flat colour, then back.

The only one of the four that reads the frame again rather than painting over what it was handed: it snaps the point it reads to a grid, so one block's worth of screen all comes back the same colour.

Two numbers here are not arbitrary and both were arrived at by looking at it:

  • The block size is what moves in a straight line, not the number of blocks. Size is one over count, so a straight line in size is a doubling of the count each step, which is what reads as the picture breaking down. Moving the count instead crawls for most of the transition and then jumps at the end.
  • The colour comes in on amount * amount, so the blocks are seen for a good while before the colour swallows them. Bringing it in straight hides the effect under the paint.

At rest it snaps to the centre of each pixel, which reads back exactly what was there: with nothing to cover it changes nothing.

Parámetros

durationnumber

How long the whole thing takes in milliseconds, breaking down and coming back.

colorTColor

What it flattens into once the blocks are as big as they get.

minBlocksnumber

How many blocks across the short side of the screen at the worst of it. Six.

Ver el código · transition/builtin/pixelate.ts:35

SCENE_FORMATvariablenacatamalon

SCENE_FORMAT: 'nacatamalon-scene'

What this format is called on disk, so a file can say what it is.

It is checked on the way in, and a file that does not say this is refused rather than half read. That sounds obvious and it is the thing the draft this engine replaces got wrong: its scene documents carried no discriminant at all, so a .scene.json could not be told apart from any other JSON by looking at it, and the only way to know was where it was found.

Ver el código · scene/document/types/t_scene_doc.ts:17

SCENE_VERSIONvariablenacatamalon

SCENE_VERSION: 1

The version of this format, raised when a document written today would be misread by an older engine.

It is read, not merely written. A document claiming a higher number is loaded anyway and warned about once, because refusing outright helps nobody: most of a newer document is still readable, and what is not lands in extra rather than being lost.

Ver el código · scene/document/types/t_scene_doc.ts:31

sceneFromDocfunctionnacatamalon

sceneFromDoc(doc: TSceneDoc, src: string, options: TSceneFromDocOptions) => TSceneFn

Turns a scene document into an ordinary scene function.

That is the whole design, and it is what keeps this to one code path. A scene from a file is handed to createGame like any other, and from then on change, a transition, a pause and a destroy all work on it without knowing where it came from. The body it returns calls the same createSprite, createText and createMesh a person would have written, so the format cannot express anything the engine's own API cannot build. A second path that assembled records by hand would be a second path free to drift from the first.

Nothing is thrown. A missing asset costs you that asset and not the scene, because a level that refused to open over one broken path is a level nobody can fix.

Parámetros

docTSceneDoc

The document, as parseSceneDoc gave it back.

srcstring

Where it came from, named in any warning it causes later.

optionsTSceneFromDocOptions

How its behaviours are treated: see TSceneFromDocOptions.

Ejemplo

const response = await fetch('/scenes/level1.scene.json');
const doc = parseSceneDoc(await response.json(), '/scenes/level1.scene.json');

createGame('#app', { width: 320, height: 240 })({ Level1: sceneFromDoc(doc) });
Ver el código · scene/document/scene_from_doc.ts:676

useScenefunctionhooknacatamalon

useScene() => TSceneHandle

Returns the controls for the game's scenes: launch, stop, change, pause, resume and isPaused.

Call it in the scene body, keep the handle, and use it later from useUpdate: it holds the game and the scene it was called in, so it keeps working after the body is over.

Ejemplo

export const Intro: TSceneFn = () => {
    const scene = useScene();
    let elapsed = 0;
    useUpdate((dt) => {
        elapsed += dt;
        if (elapsed > 3) scene.change('Level');
    });
    return createScene();
};
Ver el código · hooks/scene/use_scene.ts:98

wipefunctionnacatamalon

wipe(duration: number, direction: TWipeDirection, color: TColor) => TTransition

A straight edge crosses the screen, and crosses it again the same way to uncover.

The direction travels to the card as a vector, not as the word it was written with. A shader that branched on a name would need one branch per direction and a fifth for anything mistyped; projected onto an axis, all four are one line of arithmetic and a diagonal would cost nothing.

This is the one built-in that does not ask coverAmount(). It reads progress and phase itself and flips which side of the edge is covered, so the edge goes on travelling the same way in both halves. Taking the leftover of progress instead would bring the edge back the way it came.

Parámetros

durationnumber

How long the whole thing takes in milliseconds, covering and uncovering together.

directionTWipeDirection

Which way the edge travels. To the right unless you say otherwise.

colorTColor

What the covered side is painted. Black unless you say otherwise.

Ver el código · transition/builtin/wipe.ts:42

TAssetEntrytypenacatamalon

TAssetEntry: { type, key, source } | { type, key, src } | { type, key, json, atlas } | { type, key, src } | { type, key, src } | { type, key, src } | { type, key, src } | { type, key, src }

One thing the scene needs loaded before it can be built, named by the key everything in the document refers to it by.

The manifest exists so the loading can start before the tree does. Every loader in this engine registers its slot the moment it is asked and fills it in later, so walking this list first means that by the time a sprite says "my texture is hero", hero is already there, possibly still arriving. Without it, the first frame of a scene would be the frame that discovers what it needs.

A file is named, never inlined. The pixels of an image, the text of a shader, the cells of a map and the numbers of an effect all stay in their own file. A document that carried them would be a document that goes stale the moment somebody edits the file, and a diff nobody can read.

key is the name and src is where it is. They are usually the same string, and they are two fields because they are two different jobs: one is what the scene calls it, the other is where it is fetched from. Two scenes naming one file the same thing share one fetch.

Ver el código · scene/document/types/t_asset_entry.ts:26

TBoxNodetypenacatamalon

One box, written down: an identity, a place, what it is, and what is under it.

Propiedades

idstring

Stable for the life of the box, and the only thing anything outside the engine can address it by. A tool that remembers a selection, an undo step or a link between two boxes remembers this, so regenerating it on load would quietly break every one of them.

namestring

What it is called where it was placed. Not unique: two enemies can share it.

transformTTransform3d | null

Where it is, which moves everything under it.

null means it is not anywhere in particular, which is what an empty box used only to group things should cost: nothing. Writing an identity transform instead would make every grouping box pay for a placement it does not have and cannot use.

componentsTComponentDoc[]

What this box is: its picture, its shape, its camera, its lamp.

childrenTBoxNode[]

The boxes placed relative to it. A child is another object, never a part of this one.

dataopcionalTDataEntry[]

The state the box asked for with useData, in the order it asked for it.

Absent when there is none, which is most boxes: a data: [] on every node in a level would be noise in the file and one more thing to read past.

visibleopcionalboolean

Absent means drawn. Only ever written when it is false.

screenSpaceopcionalboolean

Absent means it lives in the world. Only ever written when it is true.

extraopcionalRecord<string, unknown>

Whatever else the file said about this box, kept exactly as written and not acted on.

Two things land here, and both would otherwise be lost. A field from a newer version of this format, which this engine cannot use but has no business deleting. And a field belonging to somebody else entirely: a tool's link back to a reusable box, a note an importer left. The engine ignores all of it and writes it back out untouched.

This is not the same mistake as inventing a field nobody reads. The difference is who wrote it: throwing it away would quietly rewrite somebody's file the first time a tool saved it, and that is exactly how the draft this replaces lost the link between a scene and the reusable boxes it was built from.

Ver el código · scene/document/types/t_scene_doc.ts:57

TCamera2dComponenttypenacatamalon

The camera the flat half of a scene is seen through.

This one carries its own placement, and it is the exception to the rule above. A camera is what everything else is measured against, so nothing above it in the tree moves it; putting its position on its box would claim otherwise, and the claim would come true the moment somebody gave that box a parent with a transform of its own.

Propiedades

type'camera2d'
idstring
transform{ x, y, rotation }
zoomnumber
Ver el código · scene/document/types/t_component_doc.ts:419

TComponentDoctypenacatamalon

TComponentDoc: TSpriteComponent | TTextComponent | TMeshComponent | TTilemapComponent | TParticlesComponent | TParticles3dComponent | TCamera2dComponent | TCamera3dComponent | TFogComponent | TLightComponent | TScriptComponent | TSoundComponent | TMusicComponent | TAudioListenerComponent | TStoreComponent | TPhysicsBody2dComponent | TPhysicsBody3dComponent | TPhysicsWorld2dComponent | TPhysicsWorld3dComponent | TParticleCollider2dComponent | TParticleCollider3dComponent | TSpriteTextureComponent

One thing a box is: its picture, its shape, its camera, its lamp.

Three rules hold across every member here, and each one is a format migration avoided:

  • A drawable carries no placement. Where a picture is belongs to its box, so a sprite and the shadow under it move together by saying it once. A camera and a lamp are the exception and for one reason: composition deliberately does not reach them. Nothing moves a camera, because it is what everything else is measured against; and a lamp is moved but never turned, so that a lantern held by someone who spins does not sweep the room. What the tree cannot decide, the component has to carry.
  • A file is named, never inlined. Shader source, atlas tables, map cells and effect numbers all stay in their own file and appear here as an asset key.
  • A field left out is a field at its default. There is no second convention: visible absent means visible and flipX absent means not flipped, because those are what they start at.

The list is uniform even though the runtime keeps typed slots for a camera and a light. A second light on one box is not an error anywhere in the engine (the last one wins, quietly), and a format that could not write that would be a format that cannot describe what the engine allows.

Ver el código · scene/document/types/t_component_doc.ts:113

TDataEntrytypenacatamalon

One piece of state a box asked for with useData, written down.

Matched by position, which is the whole design and its whole risk. There is no name to match on because useData does not take one: the first useData in a body is the first entry here, the second is the second. Reading a scene back therefore assumes the body still asks for them in the same order, and adding one in the middle of a body that has already been saved hands the old values to the wrong places.

Propiedades

valueunknown
Ver el código · scene/document/types/t_scene_doc.ts:46

TFogComponenttypenacatamalon

The fog a scene is seen through. It belongs to the view the way the camera does, so it is written on the box the camera is: the scene's root, or a box drawn into a picture.

Propiedades

type'fog'
idstring
colorTColor
nearnumber
farnumber
enabledopcionalboolean

Left out when on, which is the default.

Ver el código · scene/document/types/t_component_doc.ts:453

TLightComponenttypenacatamalon

A lamp.

Its own placement, and five fields of it rather than nine. A lamp is moved by the tree and never turned by it, so where it ends up is its own place composed with its box's, while which way it faces stays exactly what is written here. A lamp has no scale and no roll, so writing those would be writing down three numbers that are structurally always one and one that is always zero.

An ambient light is the whole room at once: no place, no direction, no reach.

Propiedades

type'light'
idstring
kind'directional' | 'point' | 'spot' | 'ambient'
colorTColor
intensitynumber
transformopcional{ x, y, z, rotationX, rotationY }

Where it is and which way it shines. Not on an ambient one, which is everywhere at once.

ambientopcionalnumber

How much of this light reaches a surface facing away from it. Not on an ambient one.

rangeopcionalnumber

How far it reaches. Point and spot only.

angleopcionalnumber

How wide the cone is, and how soft its edge. Spot only.

penumbraopcionalnumber
castShadowopcionalboolean

Whether the scene's shadows are drawn from this one. Left out is no.

Written down because it is a decision somebody took, and because it is the kind of decision that disappears quietly: a light that came back without it still lights the scene exactly as before, and the only thing missing is every shadow in the level.

shadowBiasopcionalnumber

How far the shadow test is pushed off the surface, against a surface striping itself.

shadowStrengthopcionalnumber

How dark what the light cannot see goes, 0 to 1.

shadowAreaopcionalnumber

How far the shadows reach in front of the camera. Directional only.

shadowDistanceopcionalnumber

How far back the light stands to look at that square. Directional only.

Ver el código · scene/document/types/t_component_doc.ts:479

TMaterialDoctypenacatamalon

What a material is, written down.

Two ways in, and the first is the one to use: effect names a .wgsl file, and the hooks below are left empty. The source itself is only written when there is no file to name, which happens for a material built in code with its shader typed at the call site.

That preference is the same one a script gets: the file is the truth, and a copy of its contents living in a scene is a copy that goes stale the moment somebody edits the file. A document that carried compiled shader source would be as wrong as one carrying a compiled script.

There is no signature here. Which kind each knob is, is worked out from the values themselves on the way back in, so writing it down would be saying the same thing twice, and two statements of one fact are two statements that can disagree.

Propiedades

namestring | null
effectstring | null

The .wgsl it came from, by asset key. When this is set, the four hooks below are null.

fragmentstring | null
fragmentGlslstring | null
vertexstring | null
vertexGlslstring | null
uniformsTUniformValues | null

What its knobs are set to. null when it has none.

Ver el código · scene/document/types/t_component_doc.ts:32

TMeshComponenttypenacatamalon

A shape in three dimensions.

There is no skeleton here. A rig belongs to the model file it was read out of, and it is adopted from there when the file lands: writing it into a scene would be writing down a copy of something the artist owns, in a place they will never look for it.

Propiedades

type'mesh'
idstring
transformopcionalTTransform3d

Where it sits on its box, in three dimensions, left out when that is the box's own origin.

geometrystring

The shape, by asset key.

uniformsopcionalTUniformValues
visibleopcionalboolean
zIndexopcionalnumber
castShadowopcionalboolean

Whether it is drawn into the shadow map. Left out casts, which is what nearly everything should do; false is the floor, a decal, a blob shadow's own disc.

Only false is ever written, by the rule this whole format keeps: a field that is absent is its default, so writing true would record a decision nobody took.

Ver el código · scene/document/types/t_component_doc.ts:254

TMeshMaterialDoctypenacatamalon

TMeshMaterialDoc: TMaterialDoc & { texture, tint, emissive, specular, shininess, alpha, smooth, wrap, vertexSnap, affine }

A model's material, which is a material plus a surface: what the light finds when it gets there.

A sprite's has none of this, and that asymmetry is deliberate rather than an omission. In the plane, the colour, the sheet and how it is read belong to the object: two sprites of one character tinted differently are two sprites, not two materials. In three dimensions they belong to the surface, because that is what a light is asking about.

Ver el código · scene/document/types/t_component_doc.ts:61

TMusicComponenttypenacatamalon

A piece of music in layers this object carries: the same tune in several files, played together, each at its own volume.

Written like a sound: the files by asset key and how loud each layer is meant to be, never how far into the tune it was.

Propiedades

type'music'
idstring
layers{ name, audio, volume }[]

In the order they were given. A layer's name is what the game moves it by.

volumeopcionalnumber
channelopcionalstring

Absent for 'music'.

autoplayopcionalboolean
Ver el código · scene/document/types/t_component_doc.ts:635

TParticles3dComponenttypenacatamalon

TParticles3dComponent: Omit<TParticlesComponent, 'type' | 'transform'> & { type, transform }

An emitter in three dimensions: the flat one's fields, placed in space.

Its own type rather than a flag on the flat one, for the reason the two physics bodies are apart: the file it names is a different kind of thing, and a component that could name either would make pointing it at the wrong one impossible to report.

Ver el código · scene/document/types/t_component_doc.ts:399

TParticlesComponenttypenacatamalon

An emitter.

Nothing about how the effect behaves is here: the rate, the lifetimes, the curves and the shape of the emission are all in the .particles file, which is what makes three torches one document and one picture. What a scene decides is where it is, what colour it is laid in, whether it starts lit, and how this one differs in size or speed from the file's own.

emitting and paused are deliberately absent. They are what the effect is doing right now, and a document records what was authored: autoplay is the authored intent, and a scene saved while an explosion happened to be mid-burst must not come back permanently mid-burst.

Propiedades

type'particles'
idstring
transformopcionalTTransform2d

Where the emitter sits on its box, left out when that is the box's own corner.

namestring
effectstring

The effect, by asset key.

tintTColor
alphanumber
autoplayboolean

Whether it starts lit. The authored intent, never what it is doing at the moment of saving.

seednumber | null

Fixed, so the same cloud comes back every time. null takes it from the clock.

overridesopcionalTParticleOverrides
smoothopcionalboolean
visibleopcionalboolean
zIndexopcionalnumber
Ver el código · scene/document/types/t_component_doc.ts:360

TPhysicsBody2dComponenttypenacatamalon

TPhysicsBody2dComponent: { type } & Omit<TPhysicsBody2d, '_type'>

A flat collider, written down: what this object is made of, physically.

A component and not a child object, because it is something the object is, the same as its picture and its sound. Where it is comes from the object, by the standing rule that a component carries no placement of its own, which is also why moving the object moves its collider.

The engine can keep this, write it and read it back while being completely unable to move it: a scene with physics opens and saves losslessly with nothing installed to simulate, and a tool can draw a collider's outline without loading a physics engine.

Ver el código · scene/document/types/t_component_doc.ts:714

TPhysicsWorld2dComponenttypenacatamalon

TPhysicsWorld2dComponent: { type } & Omit<TPhysicsWorld2d, '_type'>

The flat simulation's settings, written down. Lives on the scene's root: gravity belongs to the scene rather than to anything in it, and the root is an object like any other, so it needs no new home in the file.

Only the root's is read, the same way only one camera is.

Ver el código · scene/document/types/t_component_doc.ts:737

TSceneDoctypenacatamalon

One scene, written down.

The unit is the scene and never the game. What belongs to the whole game (the size of the window, its background, the input map, the look of the screen) is the project's, because "this game looks like a Mega Drive" is not a fact about whichever room happens to be open.

The three fields, in the order they are read:

  1. format and version say what this is, so a file that is not one of these is refused instead of half read.
  2. assets is everything the scene needs fetched, named. It is walked first, before a single box is built, so that by the time something says "my texture is hero" the slot for hero already exists, possibly still arriving.
  3. root is the tree.

Propiedades

formatquery
versionnumber
namestring

What the scene is called, and what it is started by.

Here rather than smuggled in as the root box's name, which is where the draft this replaces kept it. A scene in this engine is named by the key it was registered under, and createScene() deliberately takes no name at all, so without this field a document would have no way to say what it is and would only be identifiable by its file name.

assetsTAssetEntry[]

Everything the scene needs fetched, by the key the tree refers to it by.

Ejemplo

{
  "format": "nacatamalon-scene",
  "version": 1,
  "name": "Level1",
  "assets": [{ "type": "texture", "key": "hero", "src": "/assets/hero.png" }],
  "root": {
    "id": "root", "name": "Level1", "transform": null, "components": [],
    "children": [{
      "id": "player", "name": "Player",
      "transform": { "x": 40, "y": 120, "z": 0, "rotation": 0, "rotationX": 0, "rotationY": 0, "scaleX": 1, "scaleY": 1, "scaleZ": 1 },
      "components": [{ "type": "sprite", "id": "art", "texture": "hero", "tint": { "r": 1, "g": 1, "b": 1, "a": 1 } }],
      "children": []
    }]
  }
}
Ver el código · scene/document/types/t_scene_doc.ts:154

TSceneFntypenacatamalon

TSceneFn: () => TScene

A scene: a function that runs once, when the scene starts, and returns createScene(). What it makes and asks for in between (sprites, useUpdate, useKeyboard...) is what the scene has.

Ver el código · scene/types/t_scene_fn.ts:11

TSceneFromDocOptionstypenacatamalon

What sceneFromDoc can be told besides the document.

Propiedades

scriptsopcional'run' | 'attach'

Whether the objects' behaviours run, or are only recorded as attached.

'run' is the default and is what a game is. 'attach' is for showing a scene rather than playing it: each object still carries its behaviours and still saves them, and none of them is called, so nothing walks away from where it was put. A behaviour that asked to be seen while building runs in both.

Ver el código · scene/document/scene_from_doc.ts:634

TSceneHandletypenacatamalon

What a scene can do with the game's scenes, returned by useScene. Every name is one of the keys passed to createGame. Where the name is optional, leaving it out means the scene that called useScene.

Propiedades

launch
stop
change
pause
resume
isPaused
Ver el código · hooks/scene/use_scene.ts:16

TScriptComponenttypenacatamalon

A behaviour this object carries: which one, and what it was set to.

It holds a name and not code, for the same reason a sprite holds the name of its picture: a file cannot carry a function, and the behaviour lives in the project's own source where somebody can read and change it. What is written down is the link.

Propiedades

type'script'
idstring
refstring

The behaviour's name, as registerScript was given it.

propsopcionalTScriptProps

What this attachment was set to, which is what turns a fixed behaviour into a configurable one: two objects can carry the same patrol at different speeds.

Absent when the behaviour declares no settings, rather than an empty object. A behaviour with nothing to tune is the common case and its entry should stay as narrow as it ever was.

Only the values are written. Which keys exist is the behaviour's to say, and it says so in code, so a setting it gains later is filled from its own default rather than from here.

Ver el código · scene/document/types/t_component_doc.ts:539

TSoundComponenttypenacatamalon

A sound this object carries.

Unlike everything else on the list there is nothing to look at, and that is exactly why it has to be written down: a torch that crackles and a level with music are things the objects are, and a format that could only describe what is drawn would leave half of a game unauthorable.

What is written is how it was asked to play, never what it is playing. A scene saved while the music was halfway through opens ready to start it, because where a voice had got to is a fact about one run and a level is not. The knobs go the other way: a volume an options screen turned down is written down turned down, the same as a sprite is written with the tint it has rather than the one it was made with.

Propiedades

type'sound'
idstring
audiostring

The clip, by asset key.

volumeopcionalnumber
loopopcionalboolean
rateopcionalnumber

How fast it plays, which also changes the pitch.

channelopcionalstring

Which volume group it belongs to, so an options screen can move all the music at once without knowing what is in the level.

Core's format has no such field and it should: which group a sound belongs to is a decision somebody took while authoring, not a detail of playing it back.

autoplayopcionalboolean

Starts as soon as the file is there, with nothing having to ask.

spatialopcionalboolean

Comes from a side and fades with distance, following the object it hangs off.

refDistanceopcionalnumber

How far it is heard at full volume. Placed sounds only. In the scene's measure (pixels, or units under a perspective camera), and absent to take that measure's own default.

maxDistanceopcionalnumber

Past this distance it is not heard. Placed sounds only, and absent for the default.

coneopcionalTSoundCone

Louder ahead of the object than behind it. Placed sounds only.

zoneopcionalTSoundZone

Fills an area around the object instead of coming from a point. Wins over spatial.

Ver el código · scene/document/types/t_component_doc.ts:576

TSpriteAnimationDoctypenacatamalon

A sprite's animation as a document keeps it: which run of its sheet it starts on and how fast. The runs themselves are in the .atlas file and are never copied here.

Propiedades

autoplaystring | null

The run to start on, or null to rest on the sprite's frame.

speednumber

How fast, where 1 is the run's own fps.

Ver el código · scene/document/types/t_component_doc.ts:203

TSpriteComponenttypenacatamalon

A flat picture.

width and height may be left out, and leaving them out is not the same as writing the numbers the texture happens to have: it means take the texture's size, decided when the texture lands. Writing today's numbers down would freeze a sprite at the size of the art it had when it was saved.

Propiedades

type'sprite'
idstring
transformopcionalTTransform2d

Where it sits on its box, left out when that is the box's own corner.

A box places the whole object and a drawable places itself inside it, and both are real: a box can carry a picture and the shadow under it, each at its own offset. It is also what createSprite({ transform }) writes into, which is how nearly every scene in this engine is written, so without this field a room saved from a running game comes back with everything piled at the origin, having failed at nothing.

texturestring | null

The sheet, by asset key.

atlasopcionalstring

A sheet cut into a grid, by asset key, and which cell of it to show.

frameopcionalnumber
animationopcionalTSpriteAnimationDoc

The run of atlas it plays from the start, if any. Needs atlas: the runs are the sheet's.

widthopcionalnumber
heightopcionalnumber
tintTColor
anchoropcional{ x, y }
uvOffsetopcional{ x, y }

Which part of the sheet to show, when it is not cut by an atlas.

uvScaleopcional{ x, y }
flipXopcionalboolean
flipYopcionalboolean
smoothopcionalboolean
visibleopcionalboolean
zIndexopcionalnumber
materialopcionalTMaterialDoc
uniformsopcionalTUniformValues

This sprite's own values for that material's knobs, laid over the material's own.

Ver el código · scene/document/types/t_component_doc.ts:148

TSpriteTextureComponenttypenacatamalon

A picture the object and everything under it is drawn into, instead of the screen.

The picture is named by key, and that is how a model shows it: its surface asks for key the way it asks for a loaded image. It is made before any object in the scene, so a model that comes earlier in the file than the screen it shows still finds it.

sees is written only when it is 'scene', the way every field at its default is left out.

Propiedades

type'sprite-texture'
idstring
keystring
widthnumber
heightnumber
backgroundTColor
seesopcional'scene'
Ver el código · scene/document/types/t_component_doc.ts:795

TStoreComponenttypenacatamalon

An object saying that it uses one of the game's stores.

The important word is uses. A store is not in the object and cannot be: it outlives every scene, and one living inside an object would die with its scene and be two the moment a second object named it. So this carries a name and nothing else, exactly as a behaviour does, and the store itself lives in its own file and in the engine's index.

It buys two things an ordinary import cannot. A behaviour attached in an editor can be handed the state through the scene (storeOf(self)), so a scene put together by hand can wire behaviour to state with no file to write. And the tree becomes able to answer "what touches this state?", which nothing else could.

Propiedades

type'store'
idstring

Worked out from the object and the name rather than kept, which is the one identity in this format that is not: a link has exactly one field, so two links to one store on one object are the same link, and deriving it keeps a file identical through a round trip instead of issuing a fresh id on every save. The object's own id is part of it so that duplicating an object does not give two components the same name.

refstring

The store's name: what its file is called, and the key createGameStore was given.

Ver el código · scene/document/types/t_component_doc.ts:683

TTilemapComponenttypenacatamalon

A map.

One of these per map, not per layer, and that is what the engine can actually be asked for: createTilemap reads the file and puts every layer of it on the box in one go. A document with one entry per layer would describe something no call can make.

The cells are not here either: they live in the .tilemap, which is the file a person edits and a map editor writes. What a scene decides is where the map sits, how it is tinted, how its sheet is read, and then whatever it changed about individual layers.

transform is the map's own and is here rather than on the box, for a reason worth knowing: a map is cached and shared, so its placement is shared too, and two scenes showing one level are looking at the same object. Writing it as the box's placement would say each scene had its own.

Propiedades

type'tilemap'
idstring
mapstring

The map, by asset key.

transformopcionalTTransform2d

Where the map's top-left corner sits. Shared by its layers: they sort apart, not move apart.

tintopcionalTColor

Multiplies the colour of every layer.

smoothopcionalboolean
layersopcionalTTilemapLayerOverride[]

What this scene changed about individual layers, by the name the file gave them.

Absent, and absent for a layer, means the layer is exactly as the file has it. So a map dropped into a scene and left alone is four fields, not one entry per layer of description repeating what the file already says.

Ver el código · scene/document/types/t_component_doc.ts:298

TTransitiontypenacatamalon

A way of getting from one scene to another: a full-screen effect plus how long it lasts.

The same shape a post effect has, plus a duration, and that is deliberate. Writing your own transition should cost exactly what writing your own effect costs, because it is the same hook over the same finished picture. What the engine adds is not the shader, it is the lifecycle: the scene coming in exists without being seen or updated, and the swap waits for the screen to be covered and for its assets to land.

The hook is handed progress, which runs 0 to 1 twice, and phase, which says which of the two runs it is in: 0 while the screen is being covered, 1 while it is being uncovered. Both halves count up, never down, because an uncover that counted back down would play a wipe in mirror image.

Propiedades

nameopcionalstring

What a warning about it will call it, and what an editor lists. Never an identity.

durationnumber

How long the whole thing takes, in milliseconds, covering and uncovering together.

So fade(300) covers for 150 and uncovers for 150, and a stopwatch agrees with the number that was written. Each half still runs progress from 0 to 1 over its own 150.

Zero is allowed and means the screen is covered for exactly as long as the incoming scene takes to load, which is a loading screen with no animation and a perfectly good thing to ask for.

fragmentstring

fn effect(color: vec4<f32>, uv: vec2<f32>) -> vec4<f32>, and anything it calls.

fragmentGlslopcionalstring

The same hook in GLSL.

Without it there is no transition on WebGL2 and no lifecycle either: the change is made as the hard cut it was before, with a warning. A cover nobody can draw is worse than no cover, because the scene coming in would sit held back and invisible for the whole duration and then appear all at once.

uniformsopcionalTUniformValues

The knobs, and what they start at.

uniformSigopcionalTUniformSignature

What kind each one is. Worked out from the values when left out.

Ejemplo

const scene = useScene();
scene.change('Level2', { transition: fade(300) });
scene.change('Level2', { transition: wipe(400, 'right') });
Ver el código · transition/types/t_transition.ts:28