Общие пакеты
Эти пакеты можно импортировать в свой код. Здесь — что где можно использовать и что даёт каждый пакет.
В коде объекты, которые вы видите в редакторе, называются сущностями (entities). Это одно и то же.
Какие пакеты можно импортировать, зависит от того, где выполняется код:
| Пакет | Что даёт | В скрипте | В плагине | В расширении компонента |
|---|---|---|---|---|
@was/ecs | сущности, компоненты, мир | ✓ | ✓ | ✓ |
@was/engine | классы компонентов | ✓ | ✓ | ✓ |
@was/signals | реактивные значения | — | ✓ | ✓ |
@was/svdt | схемы и проверка данных | ✓ | ✓ | — |
@was/utils | небольшие вспомогательные функции | ✓ | ✓ | — |
@was/ui | библиотека компонентов самого редактора | — | ✓ | ✓ |
@was/icons | набор иконок редактора | — | ✓ | ✓ |
react | React 19 | — | ✓ | ✓ |
ctx.effectСкрипт не может импортировать @was/signals напрямую. Используйте вместо него ctx.effect. Он
работает так же и останавливается, когда пропадает его объект, так что ничего не остаётся
работать впустую.
@was/signals: реактивные значения
Сигнал — это значение, которое знает, кто его читает. Когда оно меняется, обновляется только то, что от него зависит.
import { signal, computed, effect, batch, untracked } from '@was/signals';
const score = signal(0);
const doubled = computed(() => score.value * 2);
const stop = effect(() => {
render(score.value); // выполняется снова, только когда меняется score
});
batch(() => {
score.value += 1;
score.value += 1; // эффекты выполнятся один раз, а не два
});
stop();
| Функция | Что делает |
|---|---|
signal(v) | значение, за которым можно следить; читайте и записывайте .value |
computed(fn) | производное значение; пересчитывается, только когда меняется то, что оно читает |
effect(fn) | выполняется сразу и снова каждый раз, когда меняется то, что он прочитал; возвращает функцию остановки |
batch(fn) | объединяет записи, чтобы наблюдатели сработали один раз в конце |
untracked(fn) | читает значение, не создавая зависимости от него |
ref(obj) | делает реактивным весь объект, вплоть до каждого вложенного значения |
snapshot(obj) | обычная нереактивная копия |
raw(obj) | исходный объект без отслеживания |
readonly(obj) | представление, в которое нельзя записывать |
Для панелей на React есть сопутствующий пакет с useSignal, useComputed, useSignalEffect и
useLiveSignal. С ним компонент перерисовывается от сигнала без лишней обвязки.
@was/ui: компоненты редактора
Собирайте панели плагинов и расширения компонентов из тех же компонентов, что использует редактор. Тогда ваши панели выглядят как часть редактора, а не как встроенная веб-страница. В библиотеке есть такие компоненты:
Accordion · Avatar · Button · Card · Checkbox · Chip · CloseButton
DimensionInput · Draggable · Dropdown · Flag · Icon · IconButton · Input
Menu · Modal · Outside · Panel · Popover · Portal · Render · Scroll
Search · Section · Segmented · Select · Skeleton · Switcher · Tabs
Toast · Toolbar · Tooltip
Для иконок используйте @was/icons.
В панелях работают только те служебные классы, которые поставляются с библиотекой. Произвольных
значений вроде text-[13px] там нет. Для размеров вне шкалы используйте встроенный style.
@was/ecs: сущности и компоненты
В этом пакете есть Entity, Component и Space. С ним плагин читает и меняет сцену через
полный API, а не через отдельный урезанный.
Space: мир
| Вызов | Возвращает |
|---|---|
getEntity(id) | одну сущность или ничего |
hasEntity(id) | есть ли такая сущность |
getEntities() | все сущности |
queryEntities(A, B, …) | все сущности, у которых есть все эти компоненты |
makeQueryEntities(A, B, …) | тот же запрос, собранный один раз для повторного использования |
createEntity(id?, components?) | новую сущность, уже добавленную в мир |
addEntity(…e) · removeEntity(…e) | добавляет сущность в мир или убирает её |
getSystem(S) · hasSystem(S) | систему или то, есть ли она |
execute() | выполняет один кадр |
queryEntitiesТак запрос «всё, у чего есть свет и трансформация» выполняется быстро. getEntities() отдаёт
весь мир, и фильтровать его придётся самому.
Entity: один объект в мире
| Вызов | Что делает |
|---|---|
getComponent(Type) | возвращает один компонент или null |
getComponents(A, B) | возвращает несколько сразу, в том же порядке |
hasComponent(A, B) | проверяет, есть ли все эти компоненты |
addComponent(…c) | добавляет компоненты; тип, который уже есть, пропускается |
removeComponent(…c) | убирает компоненты по классу или по экземпляру |
component(fn) | выполняет fn для каждого компонента — и нынешнего, и будущего |
clone() · clean() | копирует сущность или убирает все её компоненты |
.id · .components | её id и её компоненты по типам |
Component: данные
| Вызов | Что делает |
|---|---|
.x или get('x') | читает поле; внутри эффекта ещё и подписывается на него |
$data | весь компонент как обычный объект |
$rawData | хранимый объект без подписки; только для чтения |
update({ … }) | записывает данные. Записывать можно только так. |
updateAt(path, value) | записывает глубоко внутри большого компонента, не проверяя его заново целиком |
version(field?) | счётчик, который растёт при каждом изменении, — так можно зависеть от поля, не читая его |
reset(data?) | возвращает значения по умолчанию |
clone() | делает копию |
version()Если прочитать большое значение, вы подписываетесь на каждый элемент внутри него. Лучше зависьте от его счётчика версий: вы узнаете, что оно изменилось, не следя за каждым элементом. Так панель остаётся быстрой со списком из двадцати тысяч элементов.
@was/engine: классы компонентов
Каждый тип компонента — это класс: TransformComponent, MaterialComponent,
RigidBodyComponent и остальные. Импортируйте класс и передайте его в getComponent,
hasComponent или query.
Полный список со всеми полями — в справочнике компонентов.
@was/svdt: схемы
С этим пакетом можно описать структуру своих данных и проверять их.
import { s, compile } from '@was/svdt';
const schema = s.object({ speed: s.f64(1), name: s.string('') });
const codec = compile(schema); // компилируйте один раз, там же, где объявляете, а не при каждом вызове
const value = codec.parse(input);
Как описать структуру
- Числа:
s.f32 s.f64 s.i8 s.u8 s.i16 s.u16 s.i32 s.u32, у каждого есть значение по умолчанию. - Простые значения:
s.bool,s.string,s.color,s.literal,s.enum,s.unknown,s.ref. - Векторы:
s.vec2,s.vec3,s.mat4. - Составные:
s.object,s.variant,s.array,s.record,s.union,s.preprocess,s.lazy.
К любому из них можно добавить цепочкой: .default(v), .optional(), .nullable(), .min(n),
.max(n), .int().
Как пользоваться скомпилированным кодеком
| Вызов | Что делает |
|---|---|
parse(input) | проверяет и нормализует; при плохих данных бросает ошибку |
safeParse(input) | то же самое, но возвращает успех или ошибку, а не бросает её |
parseAt(path, v) | проверяет одно поле, не трогая остальные |
equals(a, b) | глубокое сравнение |
diff(a, b) | возвращает, что изменилось |
apply(target, p) | применяет разницу |
invert(p) | обращает разницу |
pack · unpack | переводит в компактный двоичный вид и обратно |
Чтобы изучить саму схему, а не данные, используйте introspect, keys и requiredKeys.
@was/utils
Небольшие вспомогательные функции: pick, omit, assign, keys, values, entries,
capitalize, basename, extname, dispose (объединяет функции очистки в одну) и функции для
MIME-типов, чтобы проверять типы файлов.
Дальше: Глоссарий — термины, которые встречаются в документации.