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

Собственные компоненты

Второй вид плагина вообще не открывает панель. Он прикрепляется к объекту как компонент и рисует свой интерфейс прямо в инспекторе редактора, рядом с Transform и Material.

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

Как объявить

Объявите его в 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()объект, к которому прикреплён ваш компонент
useProps()значения из формы, объявленной через defineProps
useSignals()перерисовку, когда меняются прочитанные при рендере данные сцены
useSyncedState()состояние, хранящееся на объекте и синхронизируемое как любой компонент

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

Как это исполняется

Это настоящий React, а не упрощённая имитация

Все расширения-компоненты всех плагинов работают в одном общем воркере, и React 19 внутри него. Хуки, useEffect, контекст и Suspense работают ровно так, как вы ожидаете.

Воркер выдаёт не DOM, а его описание, которое редактор рисует своими компонентами. Именно поэтому расширение выглядит родным, а не встроенной страницей.

Из этой границы следуют две вещи:

  • Набор элементов фиксирован. Вы собираете из предоставленных компонентов, а не из произвольного HTML.
  • Обработчики через границу не проходят. Редактору сообщают лишь, что обработчик есть; клик возвращается сообщением и вызывает вашу функцию в воркере. onClick пишется как обычно — это важно, только если вы рассчитывали передавать DOM-событие дальше.

Как его добавляют

Ваше расширение появляется в + New component, в группе Extensions, — к нему относятся не более особенно, чем к встроенному компоненту.

Оно появляется и в панели инструментов под +, в разделе Plugins, чтобы было очевидно, откуда взялась незнакомая кнопка. Два входа ведут себя по-разному, и это намеренно:

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

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

Доверие

Расширение-компонент запускается без открытия

Оно монтируется всякий раз, когда кто-либо открывает содержащий его проект, без всякого клика. Именно поэтому для него требуется более строгое согласие, описанное в разделе доверие и проверка.


Дальше: Горячие клавиши