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

Свои компоненты

Плагин может добавить объектам собственный компонент. Его интерфейс появится в панели Настройки элемента, рядом с Трансформацией и Материалом.

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

Объявите компонент​

Перечислите его в components в манифесте. panels может быть пустым:

{
"id": "entity-notes",
"entry": "main.tsx",
"panels": [],
"components": [{ "id": "notes", "title": "Notes", "entry": "main.tsx" }]
}

Напишите компонент​

import {
Button,
Column,
Text,
defineProps,
useEntity,
useSignals,
useSyncedState,
} from '@was/extension';
import { TransformComponent } from '@was/engine';

const useProps = defineProps({ step: { type: 'number', default: 0.5 } });

export default function Notes() {
useSignals(); // перерисовка, когда меняются прочитанные здесь данные сцены
const props = useProps(); // значения из формы в инспекторе
const entity = useEntity(); // объект, к которому прикреплён компонент
const [note, setNote] = useSyncedState('note', '');

return (
<Column gap={2}>
<Text muted>{note}</Text>
<Button
onClick={() =>
setNote(`y=${entity.getComponent(TransformComponent)?.get('position').y}`)
}
>
Remember
</Button>
</Column>
);
}

Всю работу делают эти хуки:

ХукЧто даёт
useEntity()объект, к которому прикреплён ваш компонент
useWorld()сцену, в которой находится этот объект
useProps()значения из формы, которую вы объявили через defineProps
useSignals()перерисовку, когда меняются данные сцены, прочитанные во время отрисовки
useSyncedState()состояние, которое хранится на объекте и синхронизируется, как любой компонент
useSelected()выделен ли объект в редакторе
useViewportPointer()события мыши в области просмотра — с объектом и точкой под указателем

defineProps использует те же типы полей, что и свойства скриптов: число, строка, логическое значение, цвет, выбор из списка, объект, сцена, пространство, ресурс, запись, массив и группа. Поэтому у компонента сразу появляется нормальная форма в панели Настройки элемента, без лишней работы.

Меняйте значения из компонента​

props.$set(patch) меняет собственные поля компонента так же, как инспектор: изменение синхронизируется у всех и отменяется. Значения проверяются по defineProps: числа остаются в пределах min и max, а необъявленное поле вызывает ошибку. Так можно, например, перетаскивать значение прямо в области просмотра:

<Handle axis={[0, 1, 0]} onDrag={(p) => props.$set({ height: p[1] })} />

Имена полей, которые начинаются с $, зарезервированы для этого, поэтому defineProps их не принимает.

Объявите состояние​

defineState объявляет состояние компонента со значениями по умолчанию — так же, как defineProps объявляет форму:

const useScore = defineState({ points: { type: 'number', default: 0 } });

const [score, setScore] = useScore(); // внутри компонента

Тогда скрипты читают это состояние с типами и теми же значениями по умолчанию.

Переведите его​

i18n() или useEditorLocale() из @was/extension дают t(key, vars) по словарям вашего плагина на языке редактора.

Как это работает​

Ваш компонент — настоящий React 19. Хуки, useEffect, контекст и Suspense работают как обычно.

Редактор рисует ваш компонент своими элементами. Поэтому он выглядит как часть редактора.

Из этого следуют две вещи:

  • Собирать можно только из готовых элементов. Произвольный HTML недоступен. В инспекторе: Column, Row, Text, Button, Divider, InputText, InputNumber, InputColor, Slider, Checkbox и Select. В области просмотра: Overlay, Gizmo, Group, Box, Sphere, Line, Handle и Control. Передайте Gizmo или Overlay when="selected", чтобы показывать их только на выделенном объекте.
  • Обработчики событий пишутся как обычно. onClick работает как всегда. Нельзя только куда-то передать DOM-событие.

Как компонент добавляют​

Ваш компонент можно добавить двумя способами:

ОткудаЧто происходит
+ новый компонент, группа Расширениякомпонент добавляется к уже выделенному объекту
Панель инструментов, под +, группа Плагинысоздаётся новый объект с вашим компонентом, и он выделяется

Заголовок Плагины на панели инструментов подсказывает, откуда взялась незнакомая кнопка.

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

Компонент в скриптах и в опубликованном проекте​

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

  • скрипты читают и меняют компонент через ctx.plugin('<plugin id>/<component id>') → API ctx;
  • собственный код вашего плагина в опубликованном проекте делает то же → Код в проекте.

Доверие​

Запускается без клика

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


Дальше: Код в проекте — как заставить плагин работать в опубликованном проекте.