Newer
Older
rpg / docs / engine / anim.md

Анимация: клипы, аниматоры, оживители, реестр тика

Все анимации движка тикаются от фиксированного шага (60 Гц) — Pixi-тикер не используется (sharedTicker: false), Pixi AnimatedSprite не применяется. Чистая математика (клипы, оживители, симуляция частиц) живёт отдельно от тонких Pixi-адаптеров и тестируется в Vitest без браузера.

Модули:

anim/clip.ts           чистый степпер клипов (loop/once/pingpong)
anim/SpriteAnimator.ts кадры → Sprite, именованные клипы, события
anim/FrameAnimation.ts @deprecated-обёртка совместимости над SpriteAnimator

Реестр тика: engine.fx

Эффекты, аниматоры и вспышки не требуют ручного update(dt) из сцены — добавьте объект в engine.fx (core/Updatables), движок тикает его сам между твин-ами и логикой сцены:

engine.fx.add(emitter);          // возвращает тот же объект
engine.fx.add(new SpriteMotion(view, { bob: {...} }, { seed: 7 }));
engine.fx.remove(fx);            // редко: обычно достаточно destroyed
engine.fx.clear();               // при выходе из сцены, если она не разрушает дерево

Самозачистка: объект с destroyed === true вычищается из реестра после очередного прохода (Pixi destroy() выставляет флаг сам — эффекты, живущие в разрушаемом дереве мира, отпадают без кода). Допускается и локальный инстанс Updatables внутри сцены (тот же класс, свой тик).

Клипы и SpriteAnimator

import { SpriteAnimator } from '@rpg/engine';

const animator = new SpriteAnimator(sprite, {
    walk_down: { frames: assets.frames('chars/hero_sheet.json', 'hero_down'), fps: 6 },
    idle:      { frames: [assets.frames('chars/hero_sheet.json', 'hero_down')[0]], loop: 'once' }
}, 'walk_down', { randomPhase: seed });   // seed — рассинхрон толпы

animator.play('walk_down', { restart: true }); // тот же клип без restart — продолжение
animator.pause();          // ... resume(), showStep(0) — заморозить на шаге
animator.onFrame = (step) => ...;    // смена шага
animator.onFinish = (clip) => ...;   // конец once/pingpong-прохода
animator.setClipFrames('walk_down', otherFrames); // кадры на лету (вариации окраса)

Режимы клипа (ClipLoop): 'loop' — цикл; 'once' — один раз и стоп на последнем кадре (после конца аниматор сам паузится); 'pingpong' — туда-обратно без дубля крайних кадров. Математика — чистая stepClip в anim/clip.ts: время накапливается в «шагах», dt больше периода проматывает промежуточные шаги (сообщается только финальный) — по аналогии с пересечением delay в Tween.

sprite.texture пишется только при смене шага. Флип и anchor — зона владельца спрайта (см. рецепт ходьбы в recipes.md).

FrameAnimation(sprite, frames, fps, loop) — прежний API одного клипа, оставлен как тонкая обёртка (@deprecated): setFrames(f, reset) продолжает с текущего шага без reset и стартует с нуля с ним.