tecs.gfx.animation
Fixed-step sprite-sheet playback.
A Sheet divides one image into frames and names frame spans with tags. An Animation selects a sheet and a tag and carries the speed, the loop flag, and where the cycle has got to. Playback advances on the fixed clock. The GPU resolves the shared frame table from the entity's Sprite playback encoding, so clean animated crowds perform no per-entity CPU work or instance uploads as frames change.
const hero = tecs.gfx.sheet.grid({
name = "hero",
imageWidth = 256,
imageHeight = 32,
frameWidth = 32,
frameHeight = 32,
tags = {idle = {from = 1, to = 4}, run = {from = 5, to = 8}},
})
hero:bind(tecs.gfx.images.id("sprites/hero"))
tecs.gfx.animation.plugin(world)
world:spawn(
tecs.ecs.Transform2D(64, 64, 0, 1, 0, 32, 32),
tecs.gfx.Tint(1, 1, 1, 1),
hero:sprite(),
tecs.gfx.animation.of(hero, "run"),
tecs.gfx.Renderable2D
)An entity needs a Sprite as well, because that is what playback writes and what extraction reads. An Animation on its own draws nothing.
Events#
Completed fires once when a one-shot passes its last frame, and Looped fires when a looping tag wraps. Both reach only entities carrying AnimationEvents, so a game watches a handful of animations without walking the rest of an animated crowd. Completed also clears playing on watched entities. Unwatched one-shots clamp to the last frame entirely on the GPU.
Reloads#
Re-exporting a sheet under the same name and folding it in with tecs.gfx.sheet.replace keeps every entity's sheet id, tag, and phase, so playback carries on and shows the new frames from its next step.
Module contents
Types
| Type | Kind | Description |
|---|---|---|
Animation | struct | Stores playback state for one entity. |
Completed | record | |
Looped | record | |
PlayOptions | type | Configures how an animation plays. |
Functions
| Function | Kind | Description |
|---|---|---|
frameOf | function | Returns exactly the sheet frame selected by the shared GPU table. |
of | function | Creates an animation that plays a named sheet tag, ready to spawn. |
play | function | Points a live entity at a tag and restarts it there. |
plugin | function | Installs dirty-gated GPU playback and events for AnimationEvents entities. |
restart | function | Plays an entity's animation again from the start of its tag. |
timeOf | function | Returns elapsed seconds within an entity's animation cycle. |
Values
| Value | Kind | Description |
|---|---|---|
AnimationComponent | variable | The process-wide Animation component definition. |
AnimationEvents | variable | |
DEFAULT_SPEED | variable | The rate an animation runs at when nothing says otherwise, which is the timing the sheet's frames were authored with. |
Types#
Animationstruct#
struct Animation
sheet: integer
tag: integer
speed: number
time: number
frame: integer
loop: boolean
playing: boolean
crossed: integer
endStores playback state for one entity.
time is where the cycle has got to, in seconds, and stays inside the cycle, so it neither grows without bound nor loses precision to its own age. The cycle's duration is the sum of the durations of the frames the tag visits, which tecs.gfx.sheet.Sheet.cycle reports.
Fields
sheet#
sheet: integerCaller-writable. Selects a sheet by its registration index. Zero plays nothing.
tag#
tag: integerCaller-writable. Selects a tag by its Sheet.tagId index. Zero plays the whole sheet in order.
speed#
speed: numberCaller-writable. Multiplies the timing the sheet carries. One is the timing as authored and two is twice as fast. Zero or less holds the current frame and stops time advancing, which is a pause that leaves playing alone.
How long each frame is held is the sheet's answer rather than an entity's, because that is where an artist sets it: a hold frame is a frame with a long duration, which no single rate can express.
time#
time: numberCaller-writable. Supplies the phase used when frame is zero. To seek, write time and reset frame to zero. Read live phase with timeOf.
frame#
frame: integerEngine-owned. Holds -1 for encoded GPU playback, or zero to request re-encoding from time. Ordinary game code should call frameOf, which answers for an entity carrying no Animation too. Writing zero asks playback to rewrite the Sprite, which is what play, restart, of, and a restored snapshot do.
crossed#
crossed: integerEngine-owned. Retains the serialized component layout; GPU playback leaves this legacy event scratch field at zero. Ordinary game code should observe Completed and Looped instead. The reporting system clears it, and a snapshot drops it.
Completedrecord#
Fields
Loopedrecord#
Fields
PlayOptionstype#
type PlayOptions = {
speed: number?,
loop: boolean?,
playing: boolean?
}Configures how an animation plays.
speed defaults to one, and loop and playing both default to true.
Functions#
frameOffunction#
function frameOf(borrows world: ecs.World, entity: integer): integerReturns exactly the sheet frame selected by the shared GPU table.
Arguments
| Name | Type | Description |
|---|---|---|
borrows world | ecs.World | the entity's world |
entity | integer | a live entity |
Returns
| Type | Description |
|---|---|
integer | the one-based sheet frame, or zero without a known sheet |
offunction#
function of(source: sheet.Sheet, tag: string?, options: PlayOptions?): components.FFIInstance<Animation, components.FFIComponent<Animation>>Creates an animation that plays a named sheet tag, ready to spawn.
Omit the tag to play the whole sheet in order.
Arguments
| Name | Type | Description |
|---|---|---|
source | sheet.Sheet | the sheet to play |
tag | string? | a tag the sheet names, or nil for the whole sheet |
options | PlayOptions? | the speed, loop, and playing overrides, or nil for the sheet's own timing, looping, and playing |
Returns
| Type | Description |
|---|---|
components.FFIInstance<Animation, components.FFIComponent<Animation>> | a component value ready for |
Raises
when the sheet does not carry the named tag, because an author naming a tag here has one in mind and the sheet is already in hand to check
playfunction#
function play(exclusive world: ecs.World, entity: integer, source: sheet.Sheet, tag: string?, options: PlayOptions?): nilPoints a live entity at a tag and restarts it there.
Restarting is the point: the time and the frame both reset, so the next fixed step writes the tag's first frame whatever the entity was showing. An entity carrying no Animation is given one.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world the entity lives in |
entity | integer | a live entity carrying a |
source | sheet.Sheet | the sheet to play |
tag | string? | a tag the sheet names, or nil for the whole sheet |
options | PlayOptions? | the speed, loop, and playing overrides, or nil for the defaults |
Returns
| Type | Description |
|---|---|
nil |
Raises
when the sheet does not carry the named tag
pluginfunction#
function plugin(exclusive world: ecs.World): nilInstalls dirty-gated GPU playback and events for AnimationEvents entities. One-shots clamp on the GPU without a CPU visit; add AnimationEvents when game code needs Completed or the playing flag cleared. Installing twice is harmless. Snapshots save live phases and restore against a fresh clock.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world receiving the animation systems |
Returns
| Type | Description |
|---|---|
nil |
restartfunction#
function restart(exclusive world: ecs.World, entity: integer): booleanPlays an entity's animation again from the start of its tag.
The sheet, tag, speed, and loop flag are left alone; what resets is where in the cycle playback has got to and whether it is running. For replaying a one-shot that has finished, and for rewinding one that has not.
Arguments
| Name | Type | Description |
|---|---|---|
exclusive world | ecs.World | the world the entity lives in |
entity | integer | a live entity |
Returns
| Type | Description |
|---|---|
boolean | whether there was an animation to restart; false leaves the entity untouched, since nothing there says what it would play |
timeOffunction#
function timeOf(borrows world: ecs.World, entity: integer): numberReturns elapsed seconds within an entity's animation cycle. The answer is derived from its start and the fixed clock; no component needs advancing.
Arguments
| Name | Type | Description |
|---|---|---|
borrows world | ecs.World | the entity's world |
entity | integer | a live entity |
Returns
| Type | Description |
|---|---|
number | cycle seconds, or zero without an animation |
Values#
AnimationComponentvariable#
const AnimationComponent: ecs.ComponentDefinition<Animation>The process-wide Animation component definition.
AnimationEventsvariable#
const AnimationEvents: ecs.ComponentDEFAULT_SPEEDvariable#
const DEFAULT_SPEED: numberThe rate an animation runs at when nothing says otherwise, which is the timing the sheet's frames were authored with.