Первый скрипт
Напишите небольшой скрипт, который вращает объект, когда на него нажимают. Потом добавьте настройки в инспектор и посмотрите, чем может пользоваться скрипт.
Скрипт — это файл на TypeScript, который вы прикрепляете к объекту. Он работает в плеере. Каждый объект, который использует скрипт, получает свою копию.
Вот готовый скрипт. Нажмите на объект, и он начнёт вращаться:
import { TransformComponent } from '@was/engine';
init((ctx) => {
const props = defineProps({
speed: { type: 'number', default: 1, min: 0, label: 'Speed' },
});
let spinning = false;
ctx.on('on-click', () => {
spinning = !spinning;
});
ctx.tick((dt) => {
if (!spinning) return;
const t = ctx.entity.getComponent(TransformComponent);
t?.update({
rotation: { ...t.$data.rotation, y: t.$data.rotation.y + props.speed * dt },
});
});
return () => {
// необязательно: навести порядок, когда объект пропадает
};
});
В скрипте четыре части:
| Часть | Что делает |
|---|---|
init | Запускает скрипт и даёт вам ctx — доступ к работающей сцене. Всё начинается здесь. |
defineProps | Добавляет настройки в инспектор. Вы заполняете их для каждого объекта. |
ctx.on | Реагирует на триггер, например на нажатие или столкновение. |
ctx.tick | Выполняется каждый кадр. dt — время с прошлого кадра в секундах. |
init и defineProps доступны всегда. Импортировать их не нужно. Классы компонентов, например
TransformComponent, берутся из @was/engine.
Как читать и менять объекты
const transform = ctx.entity.getComponent(TransformComponent);
transform.position; // прочитать одно значение
transform.$data; // прочитать всё целиком как обычный объект
transform.update({ … }); // записать
На двух ошибках спотыкаются почти все.
update сохраняет поля, которые вы не передали, например rotation. Но переданное значение
целиком заменяет старое. Поэтому update({ position: { y: 2 } }) заодно вернёт x и z к
значению по умолчанию — нулю.
Скопируйте значения, которые нужно сохранить, через ...:
transform.update({ position: { ...transform.$data.position, y: 2 } });
material.update({ color }) ничего не делает, потому что color лежит внутри слота. Меняйте
слот:
material.update({
materials: [{ ...material.$data.materials[0], color: '#ff0000' }],
});
Никогда не меняйте $data напрямую. Кажется, что всё сработало, но изменение потеряется.
Настройки в инспекторе
С defineProps один скрипт можно повесить на десять объектов и на каждом настроить по-своему.
Ничего не нужно копировать.
const props = defineProps({
speed: { type: 'number', default: 1, min: 0, softMax: 10, suffix: 'm/s' },
target: { type: 'entity', label: 'Look at' },
sound: { type: 'resource', resource: 'audio' },
mode: { type: 'select', options: ['chase', 'patrol'], default: 'patrol' },
});
| Тип | Что показывает редактор | Что получает скрипт |
|---|---|---|
number | поле или ползунок | число |
string | текстовое поле | строку |
boolean | переключатель | логическое значение |
color | выбор цвета | строку с цветом |
select | выпадающий список | один из ваших вариантов |
entity | выбор объекта | сам объект, готовый к работе |
scene | выбор сцены | объект сцены |
resource | выбор ресурса | ссылку, которую можно передать дальше |
array | список | массив |
group | блок с заголовком | вложенный объект |
Полезные параметры:
labelиhelp— текст, который показывается в панели.minиmax— жёсткие ограничения.softMax— где заканчивается ползунок. Число побольше всё равно можно ввести вручную.showWhen— показывает поле, только когда у другого поля определённое значение.group— помогает навести порядок в длинном списке настроек.
defineProps целикомПишите настройки прямо внутри вызова, иначе инспектор не сможет их показать. Не собирайте их из переменных и не возвращайте из функции.
Скрипт всегда получает текущее значение: в props.speed всегда то, что прямо сейчас показывает
инспектор.
Импорты
import { TransformComponent } from '@was/engine';
import helpers from 'Scripts/helpers';
Можно импортировать классы компонентов движка. А ещё можно импортировать другие ресурсы по их пути:
- другой скрипт отдаёт то, что он экспортирует;
- патч отдаёт свой код — тот же, что показывает панель Код;
- всё остальное отдаёт ссылку
{ id, type }. Передайте её вctx.spawnили передайте еёidвctx.audio.play.
Если вы переименуете или переместите ресурс, эти импорты обновятся сами.
Что скрипту недоступно
Тяжёлый скрипт не замедлит картинку. Вот что скриптам недоступно:
window,document, DOM;fetch,localStorageи любой доступ к сети;- библиотеки рендеринга — сцена меняется через компоненты, а не рисованием;
- таймеры браузера — используйте вместо них
ctx.tick.
Вместо этого используйте:
- для интерфейса — карточки интерфейса;
- для звука —
ctx.audio; - чтобы хранить значения —
ctx.storeи глобальные переменные.
Когда скрипт запускается и останавливается
Скрипт запускается, когда его объект действительно на экране: ничего выше него не выключено, а его сцена активна. Когда это перестаёт быть так, скрипт останавливается.
on-launch срабатывает сразу, как только скрипт запустился, так что вы его не пропустите.
Функция, которую вы возвращаете из init, выполняется, когда скрипт останавливается.
Используйте её, чтобы навести порядок: отписаться, остановить звуки, очистить состояние.
Редактор показывает сцену, но не выполняет никакой логики. Поправьте скрипт, а потом откройте Предпросмотр. Горячей перезагрузки нет: запущенный проект подхватит изменения при следующем перезапуске.
Дальше: Куда прикрепить скрипт — какой уровень выбрать для скрипта.