Свои компоненты
Плагин может добавить объектам собственный компонент. Его интерфейс появится в панели Настройки элемента, рядом с Трансформацией и Материалом.
Это удобно, когда инструмент относится к одному объекту: заметки к объекту, своё управление или генератор, который остаётся при том, что он создал. Панель такому плагину не нужна.
Объявите компонент
Перечислите его в 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илиOverlaywhen="selected", чтобы показывать их только на выделенном объекте. - Обработчики событий пишутся как обычно.
onClickработает как всегда. Нельзя только куда-то передать DOM-событие.
Как компонент добавляют
Ваш компонент можно добавить двумя способами:
| Откуда | Что происходит |
|---|---|
| + новый компонент, группа Расширения | компонент добавляется к уже выделенному объекту |
| Панель инструментов, под +, группа Плагины | создаётся новый объект с вашим компонентом, и он выделяется |
Заголовок Плагины на панели инструментов подсказывает, откуда взялась незнакомая кнопка.
Папка плагина, подключённая с вашего компьютера, тоже появляется в этом списке на панели инструментов, если у неё есть панель. Так её можно открыть, не заходя каждый раз в панель Плагины. Папка, в которой есть только слой поверх сцены, там не появляется: открывать нечего.
Компонент в скриптах и в опубликованном проекте
Интерфейс компонента работает только в редакторе. Его значения хранятся на объекте, поэтому проект может пользоваться ими во время работы:
- скрипты читают и меняют компонент через
ctx.plugin('<plugin id>/<component id>')→ API ctx; - собственный код вашего плагина в опубликованном проекте делает то же → Код в проекте.
Доверие
Свой компонент запускается каждый раз, когда кто-то открывает проект, где он есть. Поэтому он проходит более строгую проверку — см. Доверие и проверка.
Дальше: Код в проекте — как заставить плагин работать в опубликованном проекте.