Перейти к содержимому

Собственные объекты и глобальные зависимости

THREE, Selectable, методы трансформации и границы сохранения и экспорта собственных объектов плагина.

Плагин может создавать собственную геометрию и изучать устройство редактора. Для встроенных действий используйте стабильный editorAPI. Глобальный editor и классы редактора дают более глубокий доступ, но их устройство может меняться между версиями.

Видимый объект Three.js, редактируемый объект BDEngine и сущность Minecraft имеют разные контракты. Добавление меша в сцену решает только первую часть.

THREE и GLTFExporter

При инициализации системы плагинов доступны:

Глобальное значение Назначение
window.editorAPI Публичный API плагинов
window.editor Текущий экземпляр редактора и его внутренние подсистемы
window.THREE Three.js из сборки редактора
window.THREE.GLTFExporter Класс экспортёра glTF, добавленный к THREE
window.Selectable Базовый класс объектов редактора, наследник THREE.Group
window.libs.JSZip Подключённый редактором JSZip
gT Получение переведённого текста по ключу

GLTFExporter здесь находится именно в THREE.GLTFExporter; отдельный глобальный GLTFExporter не создаётся. Используйте экземпляр Three.js редактора, чтобы объекты, материалы и экспортёр работали с одной версией.

console.log('Three.js revision:', THREE.REVISION);
const exporter = new THREE.GLTFExporter();
const zip = new window.libs.JSZip();

Версии библиотек связаны со сборкой BDEngine. Не переносите гарантии стабильности editorAPI на любой внутренний класс или метод зависимости. Доступность глобальных объектов ещё не означает готовность интерфейса: порядок запуска плагина. Регистрация словарей и gT описаны в справке переводов.

Selectable и собственный класс

Конструктор new Selectable(editor) создаёт группу с методами трансформации, состоянием выделения и связью с редактором. Он не создаёт Minecraft-дисплей, не загружает модель и не назначает сериализатор собственному типу.

Ниже пример временного визуального маркера. Это собственная Three.js-геометрия для текущей сессии. Она не является экспортируемым блоком Minecraft.

class PreviewMarker extends Selectable {
constructor(editor) {
super(editor);
this.name = 'Маркер плагина';
this.markerMesh = new THREE.Mesh(
new THREE.BoxGeometry(0.25, 0.25, 0.25),
new THREE.MeshBasicMaterial({ color: 0xffaa00 })
);
this.add(this.markerMesh);
}
disposePreview() {
if (this.parent) this.delete();
this.markerMesh.geometry.dispose();
this.markerMesh.material.dispose();
}
}

Selectable предоставляет базовое выделение, но не включает все возможности встроенного типа: собственная группа не получает автоматически строку в панели объектов, Minecraft-экспорт или поддержку каждого инструмента. Для такой интеграции изучайте соответствующую подсистему editor и проверяйте её контракт.

Методы трансформации и selected

Описанные ниже сеттеры и геттеры синхронны. Позиция, вращение и масштаб локальны относительно родителя. Сеттеры по умолчанию помечают изменившуюся геометрию, рамки и данные пересечения для обновления, но сами не создают Undo-команду, не рассылают трансформацию в Share Party и не обновляют все панели.

У сеттеров есть skipDirty = false. В обычном плагине оставьте значение по умолчанию. true пропускает служебную отметку изменения и требует самостоятельного управления обновлениями.

Для setPosition, setRotation и setScale можно передать часть осей: отсутствующая или равная undefined ось остаётся без изменения. Переданные значения должны быть конечными числами. Строки, NaN и бесконечности вызывают ProjectValidationError; при ошибке набор значений не применяется частично.

setPosition

object.setPosition({ x, y, z }, skipDirty = false) меняет локальную позицию. Единицы - единицы сцены BDEngine, для обычных дисплеев одна единица соответствует одному блоку Minecraft. Значения округляются по object.stepPosition (по умолчанию 0.000625). Возвращает undefined.

object.setPosition({ x: 2, z: -1 }); // Y сохраняется.

getPosition

object.getPosition() возвращает новый обычный объект { x, y, z } с округлёнными локальными координатами. Изменение этого результата не меняет исходную позицию. Сам геттер позицию не переписывает.

const position = object.getPosition();
object.setPosition({ y: position.y + 1 });

setRotation

object.setRotation({ x, y, z }, skipDirty = false) принимает градусы, округляет по object.stepRotation (по умолчанию 0.01) и обновляет соответствующие углы Three.js. Возвращает undefined.

object.setRotation({ y: 90 });

В отличие от этого метода, обычное свойство Three.js object.rotation содержит радианы. Не передавайте градусы напрямую в object.rotation.y.

getRotation

object.getRotation() возвращает { x, y, z } в градусах. Если внутреннее представление ещё не создано, оно вычисляется из вращения.

В отличие от getPosition(), здесь возвращается ссылка на внутренний customRotation. Для снимка скопируйте её. Изменение этой ссылки вручную не запускает пересчёт Three.js-вращения и уведомления об изменении:

const rotation = { ...object.getRotation() };
object.setRotation({ y: rotation.y + 15 });

Углы могут сохранять накопленные обороты. Не ожидайте обязательной нормализации в диапазон от -180 до 180.

setQuaternionRotation

object.setQuaternionRotation(quaternion, skipDirty = false) копирует кватернион и обновляет представление поворота в градусах с учётом изменения относительно предыдущего кватерниона. Возвращает undefined; пустой аргумент ничего не меняет.

Передавайте нормализованный THREE.Quaternion. Проверка требует конечные компоненты x/y/z/w и ненулевую длину, но метод не нормализует присваиваемый кватернион автоматически. Невалидный кватернион вызывает ProjectValidationError.

const quaternion = new THREE.Quaternion().setFromAxisAngle(
new THREE.Vector3(0, 1, 0),
Math.PI / 2
);
object.setQuaternionRotation(quaternion);

Угол в setFromAxisAngle - радианы, а getRotation() по-прежнему возвращает градусы. Кватернион описывает ориентацию, но сам по себе не хранит число полных оборотов, поэтому не используйте его как замену многовитковым ключам анимации.

setScale

object.setScale({ x, y, z }, skipDirty = false) задаёт множители масштаба. Значения округляются по stepScale (по умолчанию 0.0001) и ограничиваются снизу minScale (по умолчанию 0.0011). Возвращает undefined. У частицы дополнительно обновляет масштаб её gizmo.

object.setScale({ x: 2, y: 2, z: 2 });

Нулевой или отрицательный масштаб не создаёт нулевой размер или отражение: он поднимается до минимального положительного значения. Для поддерживаемых объектов отражение задаётся отдельно через setMirrorX.

getScale

object.getScale() возвращает новый объект { x, y, z }. Геттер может изменить исходный объект: если текущий масштаб не соответствует округлению или минимуму, он вызывает setScale и обновляет локальную и мировую матрицы. Поэтому это не всегда чтение без побочных эффектов.

selected

object.selected - свойство состояния выделения. Присваивайте boolean. При включении создаётся визуальная рамка, при выключении она удаляется; редактор также обновляет ревизию выделения.

object.selected = true;
editorAPI.update(true);

Присваивание не снимает выделение с других объектов, не создаёт историю выделения и само по себе не выполняет полный workflow контроллера. После пакетного изменения состояния используйте editorAPI.update(true).

Скос, отражение и матрицы

Аффинная трансформация включается для объектов, у которых isDisplay или isCollection истинно. У обычного собственного наследника Selectable это не включено. Не назначайте эти флаги только ради эффекта: другие системы редактора ожидают соответствующий встроенный тип.

Метод Контракт
supportsAffineTransform() Возвращает boolean, включена ли поддержка скоса и отражения
getShear() Возвращает копию { xy, xz, yz }, углы в радианах
setShear({ xy, xz, yz }, skipDirty = false, updateMatrix = true) Обновляет указанные компоненты. Для каждой нужен конечный угол строго между -Math.PI / 2 и Math.PI / 2; неверный угол возвращает false без изменения, успех - true
getMirrorX() Возвращает boolean отражения по локальной X
setMirrorX(value, skipDirty = false, updateMatrix = true) Приводит значение к boolean, обновляет матрицу и служебные данные по флагам. Возвращает undefined
setFromAffineMatrix(matrix, options = {}) Разбирает THREE.Matrix4 в компоненты и возвращает сам объект
applyMatrix4(matrix) Применяет матрицу слева к локальной матрице, возвращает сам объект
updateMatrix() Пересобирает локальную матрицу с учётом поддерживаемых скоса и отражения; возвращает undefined

setShear допускает частичный набор осей. Значения очень близко к границе дополнительно ограничиваются до Math.PI / 2 - 0.000001 по модулю. У setFromAffineMatrix доступны referenceRotation = null (опорный THREE.Euler), skipDirty = false и updateWorld = true. Для объекта без аффинной поддержки матрица раскладывается в обычные позицию, кватернион и масштаб.

if (object.supportsAffineTransform()) {
object.setShear({ xy: THREE.MathUtils.degToRad(10) });
object.setMirrorX(true);
editorAPI.update(true);
}

Добавить объект в сцену

Созданный экземпляр подключается через editorAPI.addObject. В примере используется класс PreviewMarker выше:

const marker = new PreviewMarker(editor);
marker.setPosition({ x: 0, y: 1, z: 0 });
editorAPI.addObject(marker);
marker.selected = true;
editorAPI.update(true);
// Когда предпросмотр больше не нужен:
// marker.disposePreview();
// editorAPI.update(true);

У самого Selectable есть add(...objects) и remove(...objects), унаследованные от группы и дополненные обновлением служебных индексов редактора. Они меняют дерево и возвращают объект-родитель, но не создают историю. object.delete() снимает выделение у потомков и отсоединяет объект от родителя; он возвращает undefined и требует, чтобы родитель существовал.

Прямое удаление в примере подходит временному предпросмотру. Для штатных объектов проекта используйте операции удаления editorAPI.

Сохранение, экспорт и ресурсы

Проект сохраняет известные редактору типы через их собственное представление. Наследования от Selectable недостаточно, чтобы сериализовать произвольный класс, загрузить его обратно или восстановить после Undo. В частности, addObject не регистрирует формат и не добавляет команду истории.

Геометрия THREE.Mesh также не превращается автоматически в Minecraft-дисплей. Для своего формата экспорта можно взять снимок мешей и обработать его собственной логикой. Для полноценной интеграции типа с проектом потребуются дополнительные механизмы editor и отдельная проверка сохранения и восстановления.

Ресурсы, созданные плагином, освобождает плагин: геометрии, материалы, собственные текстуры, обработчики и таймеры. Удаление DOM-окна или отсоединение Three.js-объекта не вызывает их полную очистку. Освобождайте только то, чем владеете; общие текстуры и материалы редактора могут использоваться другими объектами.

Когда лучше встроенный объект

Если результат должен остаться редактируемым дисплеем и экспортироваться в Minecraft, начните с editorAPI.add для BlockDisplay, ItemDisplay или TextDisplay. Их загрузка и штатная сериализация уже реализованы редактором.

Собственная геометрия полезна для предпросмотра, визуальных инструментов и других возможностей плагина, где сохранение и игровой экспорт определяются отдельно. Практический путь: создать генератор встроенных объектов.

Применить на практике