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

Код в проекте

Дайте плагину код, который работает в опубликованном проекте: системы, которые двигают объекты, триггеры и шаги для событий и настройки для всего проекта.

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

Добавьте код​

Укажите в поле runtime манифеста файл .ts или .js из папки плагина:

{
"id": "acme.spin",
"name": "Spin",
"version": "1.0.0",
"entry": "main.tsx",
"permissions": [],
"panels": [],
"components": [{ "id": "spin", "title": "Spin", "entry": "spin.tsx" }],
"runtime": "runtime/index.ts"
}

Файл выполняется один раз при старте проекта, рядом со скриптами проекта. Зарегистрируйте в нём систему. Система работает каждый кадр:

// runtime/index.ts
import { Collection, System } from '@was/ecs';
import { TransformComponent, frameTime, pluginComponent } from '@was/engine';

class AcmeSpinSystem extends System.from('AcmeSpinSystem') {
execute() {
const { dt } = frameTime();
const spin = pluginComponent('acme.spin/spin', this.space);
for (const e of spin.entities()) {
const t = e.getComponent(TransformComponent)!;
const r = t.$data.rotation;
t.update({ rotation: { ...r, y: r.y + (spin.props(e)!.speed as number) * dt } });
}
}
}
Collection.registerSystem(AcmeSpinSystem);

Эта система вращает каждый объект с компонентом плагина Spin со скоростью, заданной в инспекторе.

Называйте системы по имени плагина

Имена систем общие для всего проекта. Если имя уже занято, ваша система не запустится, а в ошибке будет сказано, какое имя. Начинайте каждое имя с названия плагина, как AcmeSpinSystem.

Что доступно коду​

Код в проекте может импортировать то же, что и скрипт: @was/ecs, @was/engine, @was/utils и @was/svdt. В проект попадает только входной файл и файлы, которые он импортирует через ./, — всего до 1 МБ.

У этого кода нет страницы, DOM, three.js и React. Он читает и меняет компоненты, а движок рисует результат.

ФункцияЧто даёт
frameTime(){ dt, t }: время с прошлого кадра и с начала, в секундах
pluginComponent(key, space)ваш компонент на объектах: читать и менять его
pluginEvents(id, space)запускать ваши триггеры и отвечать на ваши шаги
pluginSettings(id)значения ваших настроек, которые выбрал проект
pluginContext(id, space)ctx скрипта: ввод, физика, лучи, звук, глобальные значения, мультиплеер

key — это <plugin id>/<component id>, например acme.spin/spin. id — поле id плагина из манифеста.

Ваш компонент​

pluginComponent(key, space) даёт:

МетодЧто делает
entities()объекты, у которых есть компонент
has(e)включён ли компонент у объекта
props(e)значения из инспектора, со значениями по умолчанию
state(e)собственное состояние компонента
setState(e, patch)меняет состояние
setProps(e, patch)меняет значения инспектора
setEnabled(e, on)включает или выключает компонент
add(e, props)добавляет компонент объекту
remove(e)убирает его

Скрипты получают то же самое через ctx.plugin(key).

Контекст скрипта​

pluginContext(id, space) возвращает тот же ctx, что получает скрипт: ввод, физика, лучи, звук, глобальные значения и мультиплеер. До старта скриптов проекта он равен null и может смениться вместе с миром, поэтому запрашивайте его, когда он нужен. ctx.on и ctx.tick добавляют новый обработчик при каждом вызове, поэтому подписывайтесь один раз на каждый контекст.

Триггеры и шаги​

Плагин с кодом в проекте может добавить свои триггеры и шаги в панель событий. Ими пользуются без кода, как встроенными. Они появляются под названием плагина в проектах, где он есть.

Объявите их в манифесте:

"triggers": [
{ "id": "reached", "title": "Reached a point",
"params": { "point": { "type": "number", "default": -1, "any": -1, "label": "Point" } } }
],
"steps": [
{ "id": "pause", "title": "Pause",
"params": { "seconds": { "type": "number", "default": 0, "min": 0 } } }
]
  • Id пишутся в kebab-case. Полное имя — <plugin id>:<id>, например acme.patrol:reached.
  • params используют типы полей defineProps: number, string, boolean, color, select, entity и resource. Панель рисует для них форму.
  • Параметры триггера фильтруют. Событие с точкой 2 сработает, только когда триггер запущен с point: 2. Пустой параметр или параметр со значением any пропускает любой запуск.

Запускайте триггеры и отвечайте на шаги из кода плагина:

import { pluginEvents } from '@was/engine';

class AcmePatrolSystem extends System.from('AcmePatrolSystem') {
events = pluginEvents('acme.patrol', this.space);
off = this.events.onStep<{ seconds: number }>('pause', (targets, params) => {
// остановить объекты из `targets` на params.seconds
});

execute() {
// когда охранник дошёл до точки:
// this.events.fire('reached', guard, { point: 2 });
}

dispose() {
this.off();
}
}
  • fire(id, object, payload) запускает события с этим триггером на объекте и на объектах выше него. Без объекта триггер срабатывает везде. Скрипты слышат его как ctx.on('acme.patrol:reached', payload => …).
  • onStep(id, handler) вызывается, когда событие выполняет ваш шаг. Скрипт тоже может его выполнить: ctx.step('acme.patrol:pause', { seconds: 2 }). Подпишитесь один раз, а возвращённую функцию вызовите в dispose().
  • Если плагин убрали из проекта, его триггеры больше не срабатывают, а шаги пропускаются.

Настройки проекта​

Некоторые значения относятся ко всему проекту: темп, сложность, цвет. Объявите их в settings: до 24 полей типов number, string, boolean, color и select.

"settings": {
"tempo": { "type": "number", "default": 1, "min": 0, "label": "Tempo" }
}

Их задают в настройках проекта, в разделе с названием плагина.

  • Код в проекте читает их через pluginSettings<{ tempo: number }>('acme.patrol'). Вы получаете все поля: значение проекта или значение по умолчанию. Значения готовы до запуска входного файла и не меняются, пока проект работает.
  • Опубликованный проект использует значения, с которыми его опубликовали.
  • Панель плагина читает и меняет их через editor.pluginSettings.get() и editor.pluginSettings.set({ tempo: 2 }). Для этого нужно право settings:write.

Как код попадает в проект​

Код работает только в проектах, в которые добавлен плагин.

  • Добавление фиксирует версию. Проект сохраняет код, который был у плагина в этот момент. Предпросмотр, плейтесты и публикации запускают этот код, даже когда вы опубликовали новый.
  • Обновление — по желанию. Когда есть код новее, у плагина в панели Плагины появляется пометка есть обновление. Кнопка Обновить в проекте переводит проект на него.
  • Ваши проекты идут за вами. Если вы добавили свой плагин в проект, проект запускает каждую версию, которую вы сохраняете.
  • Публикация хранит свой код. Опубликованный проект запускает код, с которым его опубликовали, до следующей публикации.

Названия, описания и подписи триггеров, шагов и настроек можно брать из словарей плагина — пишите их как ${…}.

Плагины для подписчиков​

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

Если подписка закончилась, проект с этим плагином больше нельзя опубликовать заново. Продлите подписку или уберите плагин. Уже опубликованный проект продолжает работать.

Для разработчиков​

  • Код в проекте выполняется в воркере скриптов проекта, вместе со скриптами проекта.
  • Версия, которую хранит проект, — это отпечаток файлов кода, триггеров, шагов и настроек. Другой отпечаток показывает пометку есть обновление.

Дальше: Горячие клавиши — клавиши, с которыми работать быстрее.