Model и ModelAnimation
Загруженная GLB-модель — свой вид узла (GLB — это иерархия узлов с запечёнными клипами анимации),
отличный от Mesh (сырые примитивы, без анимации). Расширяет
Node. Каждая Model несёт аспект ModelAnimation, заранее прикреплённый на
model.anim — сам ты его не прикрепляешь.
Обзор
const hero = await Model.load(asset('./hero.glb'))
hero.position = [0, 0, -2]
scene.add(hero)
hero.anim.play('Run', { loop: true })
hero.addEventListener('loopReached', clip => console.log('lap', clip))Загрузка
Model.load(source: string | FetchResponse, options?: {
culling?: boolean // по умолчанию false — вся модель рисуется всегда
onProgress?: (p: { loaded: number, total?: number }) => void
}): Promise<Model>source — путь asset('./file.glb'), удалённый https://… URL или уже скачанный FetchResponse.
Отклоняется при HTTP ≥ 400 или ошибке декодирования.
отсечение по фрустуму по умолчанию выключено для моделей (скиннинг-меши двигаются за пределы
своих границ времени импорта). Передай culling: true для крупных статичных пропов, чтобы части за
кадром пропускали отрисовку.
Клонирование
const enemy = template.clone(): Modelclone() делает глубокую копию GLB — меши, скелет и запечённые клипы анимации — переиспользуя уже
декодированный ассет (без повторного скачивания и парсинга), поэтому он сильно дешевле второго
Model.load. Клон прикрепляется к родителю источника и попадает в его сцену, стартуя с текущего
трансформа источника. У него своё независимое состояние анимации, доступное через clone.anim.
Отсечение по умолчанию выключено, как у Model.load.
const template = await Model.load(asset('./enemy.glb')) // декодируем один раз
const wave = Array.from({ length: 8 }, () => template.clone()) // дешёвые копии, без await
wave.forEach((e, i) => { e.x = i * 2; scene.add(e); e.anim.play('Walk', { loop: true }) })Для пулов спавна предпочитай clone() повторной загрузке того же URL — один декод, затем N
экземпляров.
Иерархия узлов
Model — корневой узел GLB; внутренние узлы файла висят под ним как обычные
Node:
model.traverse(node => { // этот узел, затем каждый потомок
if (node.name === 'Sword') node.visible = false
})
model.children // прямые детиТрансформ, visible, события, аспекты (Shape, Physics, …) работают и на корне, и на внутренних
узлах.
Анимация — model.anim
model.anim.clips // { name: string, duration: number }[] — запечено в glb (секунды)
model.anim.play(clip?: string | number, options?: { loop?: boolean }): this
model.anim.stop(): this
model.anim.playing = true // get/set — продолжить/приостановить текущий клип
model.anim.loop = true // get/set — также задаётся через опции play()
model.anim.speed = 1.5 // множитель скорости (1 = авторская скорость)
model.anim.time = 0.5 // get/set — перемотка внутри текущего клипа (секунды)play() принимает имя или индекс клипа; без аргумента (пере)проигрывает текущий клип. Проигрывание
по имени работает только когда клипы GLB осмысленно названы — проверь model.anim.clips (или назови
их в DCC-инструменте/экспортёре).
неизвестное имя клипа молча игнорируется — play('Runn') оставляет выбранный ранее клип и играет
его. Валидируй по clips, если имя приходит из данных.
События конца клипа
Срабатывают на узле (не на anim), с индексом клипа как аргументом:
model.addEventListener('completed', clip => { /* незацикленный клип завершился */ })
model.addEventListener('loopReached', clip => { /* зацикленный клип обернулся */ })После completed аспект переключается в playing = false; зацикленный клип идёт до stop().
Ограничения
- Нет кросс-фейда и блендинга —
play()жёстко переключается на новый клип в следующем кадре. Одновременно играет один клип; послойного/частичного микса тела нет.
Подводные камни
// ✗ await свежей загрузки на каждый спавн — повторное скачивание + декод, дёргается в момент спавна
const enemy = await Model.load(url) // на каждый спавн
// ✓ загрузи один раз, затем clone() — без await, без повторного декода
const template = await Model.load(url)
const enemy2 = template.clone()
// ✗ ждать плавного перехода
hero.anim.play('Idle') // резко — кросс-фейда не существуетСмотрите также
- Node — трансформ, иерархия,
traverse, события (унаследованы). - Mesh — примитивы / кастомная геометрия (другой рисуемый вид).
- Физика и формы — дать модели форму коллизии.
- Scene — добавление узлов, камера.