Node
Базовый узел 3D-сцены — пустой трансформ. Mesh, Model,
Light и Camera расширяют его, поэтому всё здесь работает и на них. Нативный
движок владеет авторитетным трансформом; SDK читает его лениво (физика может переписывать его каждый
кадр), и каждый геттер трансформа возвращает свежую копию — см.
семантику значений.
Обзор
const pivot = new Node()
pivot.position = [0, 1, -2]
pivot.eulerAngles = [0, 45, 0] // градусы
pivot.add(Mesh.box({ material: Material.lit({ color: '#38f' }) }))
scene.add(pivot)
setLoop(dt => { pivot.eulerAngles = pivot.eulerAngles.add([0, 90 * dt, 0]) })Локальный трансформ
node.position = [x, y, z] // Vec3Like; мировые единицы ≈ метры
node.x = 3 // скалярные аксессоры: x / y / z
node.quaternion = Quat.fromEuler(0, 90, 0)
node.eulerAngles = [0, 90, 0] // ГРАДУСЫ
node.scale = 2 // равномерно или [sx, sy, sz]
node.matrix = Mat4.compose([0, 1, 0], Quat.identity, 1) // полный локальный TRSГеттеры возвращают свежие копии Vec3 / Quat / Mat4 — изменение одной никогда не трогает
узел:
node.position.x = 3 // ✗ тихий no-op (меняет отброшенную копию)
node.x = 3 // ✓ сеттер одной оси
const p = node.position; p.y += 1; node.position = p // ✓ поменять локальную, присвоить обратноМировое пространство
node.worldMatrix // get/set: полный мировой трансформ (установка собирает через родителя)
node.worldPosition // Vec3 (только чтение)
node.worldQuaternion // Quat (только чтение)
node.worldEulerAngles // Vec3, градусы (только чтение)
node.worldScale // Vec3 (только чтение)
node.forward // Vec3: ось вперёд узла (−Z) в мировом пространстве (только чтение)Имя и видимость
node.name = 'door' // хранится нативно; поиск детей Model сопоставляется по нему
node.visible = false // скрывает узел (и то, что он рисует)Иерархия
node.add(...children: Node[]): this // сделать детей потомками этого узла
node.setParent(parent: Node | null, worldPositionStays = false): this
node.parent // Node | null
node.children // Node[] (массив-снимок)
node.childCount
node.getChild(index: number): Node | null
node.traverse(callback: (node: Node) => void): void // этот узел, затем все потомкиС worldPositionStays: true setParent держит узел там, где он в мире (его локальный трансформ
пересчитывается); с дефолтным false локальный трансформ сохраняется как есть.
родительство отдельно от членства в сцене — scene.add(node) кладёт узел в набор отрисовки,
node.add(child) строит иерархию трансформов. См. Scene.
lookAt
node.lookAt(point: Vec3Like, mode = '-z', ortho = [0, 1, 0]): this
// mode: 'z' | '-z' | 'x' | '-x' | 'y' | '-y' — какая локальная ось смотрит на цельОриентирует узел так, чтобы выбранная локальная ось (по умолчанию '-z', ось вперёд) смотрела на
точку в мировом пространстве. ortho — подсказка «вверх» для разрешения крена. Масштаб
сохраняется.
Аспекты
Возможности прикрепляются как аспекты и чейнятся (см. аспекты):
const ball = Mesh.sphere({ material: Material.lit() })
.aspect(Shape, { sphere: 0.5 }) // форма пикинга/коллизии → кликабелен, рейкастится
.aspect(Physics, { motion: 'dynamic' }) // твёрдое тело → node.physicsСм. 3D-физику и формы для Shape, Physics, Trigger,
CharacterController.
События
node.addEventListener(channel, callback): void
node.removeEventListener(channel, callback): void| Канал | Срабатывает | Нужен |
|---|---|---|
click |
отпускание над узлом | аспект Shape (тело-пикер) |
touchstart |
нажатие над узлом; ev.track() начинает перетаскивание |
аспект Shape |
enter / exit |
контакт физики / перекрытие триггера начались / закончились ((other: Node)) |
Shape + Physics/Trigger |
loopReached |
зацикленный клип GLB обернулся ((clip: number)) |
ModelAnimation |
completed |
незацикленный клип GLB завершился ((clip: number)) |
ModelAnimation |
track / untrack |
AR-якорь получил / потерял трекинг (также node.isTracked) |
якорь ARScene |
Без аспекта Shape узел невидим для тапов — тач увидят только слушатели уровня сцены (с
ev.target === null). Объекты событий и перетаскивания ev.track():
События указателя и жесты.
Жизненный цикл
node.destroy(): void // освобождает нативную сущность; после этого узел непригоденdestroy() — единственный способ разбора, dispose() на узлах нет. Использование уничтоженного узла
— неопределённое поведение.
Подводные камни
// ✗ изменение возвращённого значения геттера трансформа
node.position.x += speed * dt // no-op: геттеры возвращают копии
// ✓
node.x += speed * dt
// ✗ запись трансформа динамического физического тела каждый кадр
setLoop(() => { node.position = target }) // борется с шагом физики
// ✓ управляй телом: node.physics.velocity / applyImpulse (см. physics.md)Смотрите также
- Соглашения — семантика значений, градусы против радианов, единицы.
- Mesh / Model — рисуемые виды узлов.
- 3D-физика и формы — тела-пикеры
Shape,enter/exit, твёрдые тела. - События указателя и жесты — полезная нагрузка
click/touchstart, перетаскивания. - ARScene — узлы-якоря,
track/untrack.