ci-platforms / darwin arm64 smoke (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 12m19s
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
github-native / gitea-pending (push) Skipped
github-native / darwin amd64 native (push) Skipped
github-native / linux amd64 native (push) Skipped
github-native / windows amd64 native (push) Skipped
github-native / darwin arm64 native (push) Skipped
github-native / gitea-result (push) Skipped
ci / checks (push) Failing after 23m57s
ci / release (push) Skipped
GitHub / windows amd64 native GitHub-hosted native CI: failure
GitHub / darwin arm64 native GitHub-hosted native CI: failure
GitHub / darwin amd64 native GitHub-hosted native CI: failure
GitHub / linux amd64 native GitHub-hosted native CI: success
344 lines
32 KiB
Markdown
344 lines
32 KiB
Markdown
---
|
||
id: gameplay
|
||
title: Набор для геймплея
|
||
---
|
||
# Набор для геймплея
|
||
|
||
Модуль геймплея добавляет то, что большинство 2D-игр собирают вручную: контроллер персонажа, очки здоровья, хитбоксы и хёртбоксы, смертельные зоны, контрольные точки с возрождением, подбираемые предметы и удаление по таймеру. Это компоненты: их настраивают в Инспекторе, сохраняют вместе со сценой и управляют ими из скриптов несколькими вызовами.
|
||
|
||
Быстрый старт: **Создать → Геймплей → Персонаж платформера** даёт узел с кинематическим «Телом 2D», коллайдером, «Движением 2D», «Здоровьем», «Хёртбоксом 2D» и «Возрождением». Дайте ему скрипт, который вызывает `Move` и `Jump`, добавьте пол и нажмите **Играть**.
|
||
|
||
Набор используют две готовые игры. `aego new mygame --example platformer` — враги, которых давят хитбоксом на ногах, яма со смертельной зоной, контрольная точка, сохраняемая точка возрождения и «Аниматор» для облика игрока с ударом на кадре попадания. `aego new mygame --example topdown` — меч, которым машут в сторону стика, мыши или направления ходьбы, враги, чья «Машина состояний» переключается между Patrol и Chase, а «Аниматор» это показывает, выпад через root motion, монеты и лава. Лаунчер предлагает обе игры в разделе **Примеры**.
|
||
|
||
| Компонент | Назначение |
|
||
|---|---|
|
||
| Движение 2D | ходьба, прыжки и скольжение кинематического тела: склоны, ступеньки, движущиеся платформы, движение с видом сверху |
|
||
| Здоровье | очки здоровья, команда, неуязвимость после удара, смерть и оживление |
|
||
| Хёртбокс 2D | область, где хитбоксы ранят «Здоровье» |
|
||
| Хитбокс 2D | область, которая наносит урон хёртбоксам, с отбрасыванием |
|
||
| Время жизни | убирает узел через несколько секунд |
|
||
| Смертельная зона 2D | убивает или возрождает то, что в неё падает |
|
||
| Контрольная точка 2D | переносит точку возрождения |
|
||
| Возрождение | возвращает узел в его точку возрождения |
|
||
| Подбираемый предмет 2D | посылает `Collected`, когда персонаж его касается |
|
||
| Машина состояний | именованные состояния с методами Enter, Exit, Fixed и Update в скриптах узла |
|
||
|
||
Всё работает во время игры и в `aego run`, на фиксированном шаге, и ничего не выделяет на каждом шаге. Хёртбоксы, хитбоксы и зоны — области в пространстве своего узла, а не физические коллайдеры: они ничего не толкают, и слои столкновений им не нужны.
|
||
|
||
---
|
||
id: gameplay/motion
|
||
title: Движение персонажа
|
||
---
|
||
# Движение персонажа
|
||
|
||
[«Движение 2D»](ref:gameplay.Motion2D) двигает персонажа с кинематическим [«Телом 2D»](ref:physics.Body2D). На каждом фиксированном шаге оно проверяет коллайдеры тела вдоль пути, скользит вдоль того, во что упирается, и переносит тело в итоговую точку. Поэтому персонаж никогда не проваливается в стены и всё равно толкает ящики.
|
||
|
||
В интерактивном примере ходите клавишами A/D или стрелками и прыгайте клавишей Space, W или Up; подъёмник, склон и стены работают через настоящую физику.
|
||
|
||
m := node.Get<Motion2D>()
|
||
m.Move(input.Axis("Move"))
|
||
if input.Pressed("Jump") {
|
||
m.Jump()
|
||
}
|
||
if input.Released("Jump") {
|
||
m.JumpReleased()
|
||
}
|
||
|
||
`Move` действует до следующего вызова. `Jump` — один запрос, который контроллер хранит «Буфер прыжка» секунд, поэтому прыжок, нажатый незадолго до приземления, всё равно происходит.
|
||
|
||
| Режим | Поведение |
|
||
|---|---|
|
||
| platformer | ходит по полу под действием гравитации, прыгает, поднимается на ступеньки и склоны |
|
||
| topDown | свободно движется в плоскости через `MoveDir`, скользит вдоль стен; «Поворот по движению» поворачивает узел |
|
||
|
||

|
||
|
||
## Пол, склоны и ступеньки
|
||
|
||
Пол, стены и потолок различаются по углу поверхности к «Направлению вверх»: поверхность круче «Макс. уклона» — стена. На полу персонаж следует его наклону и не съезжает вниз, а «Прилипание к земле» держит его на полу при спуске по склону или с небольшой ступеньки. На ступеньки до «Высоты ступеньки» персонаж поднимается без прыжка. «Безопасный зазор» — расстояние до каждой поверхности, которое не даёт следующей проверке начаться внутри неё.
|
||
|
||
В воздухе стена останавливает движение в неё и никогда не поднимает персонажа: капсула или круг, задевшие угол уступа, соскальзывают с него, а не повисают на нём.
|
||
|
||
## Уступы
|
||
|
||
Если **Подъём на уступ** больше 0, персонаж в воздухе, который движется в стену, чей верх не выше «Подъёма на уступ» над его ногами, забирается на неё. За **Время подъёма** секунд он поднимается до верха, шагает на уступ и приземляется там. Более высокие стены он не трогает. Подъём идёт на фиксированных шагах, как и всё движение: во время него ввод, гравитация и root motion не учитываются, а скорость остаётся нулевой. Отбрасывание (`AddVelocity`), `Stop`, `Dash` и `Teleport` сразу прерывают его.
|
||
|
||
**Climbing** истинно во время подъёма, а **Climbed** срабатывает в его начале. Чтобы анимировать подъём, передавайте флаг «Аниматору» в `FixedUpdate`, как это делает игрок в примере платформера:
|
||
|
||
```aes
|
||
a := node.Get<Animator>()
|
||
a.SetBool("Climbing", m.Climbing)
|
||
```
|
||
|
||
и добавьте в контроллер состояние `Climb`, в которое переход идёт из `any`, пока `Climbing` истинно.
|
||
|
||
## Прыжки
|
||
|
||
Задайте **Высоту прыжка**, и контроллер вычислит скорость прыжка из неё и гравитации; **Скорость прыжка** вместо этого задаёт скорость напрямую. Если отпустить кнопку во время подъёма, прыжок укоротится на **Множитель короткого прыжка**, поэтому короткое нажатие даёт небольшой подскок.
|
||
|
||
Два окна прощают неточные прыжки. **Время койота** позволяет прыгнуть ещё мгновение после схода с края. **Буфер прыжка** помнит прыжок, нажатый в воздухе, до приземления. **Управление в воздухе** решает, какая часть ускорения на земле доступна в воздухе.
|
||
|
||
Все поля перечислены на странице [«Движение 2D»](ref:gameplay.Motion2D).
|
||
|
||
## Платформы и тела
|
||
|
||
С «Движущимися платформами» персонаж едет на теле, на котором стоит. «Платформа при уходе» решает, что станет со скоростью платформы после прыжка: `keep` добавляет её, `drop` теряет, `add` сохраняет только горизонтальную часть. С «Толкать тела» динамические тела не останавливают персонажа — он их толкает.
|
||
|
||
Остальное покрывают `Dash(velocity, seconds)`, `AddVelocity` для отбрасывания, `AddDisplacement(offset)` для root motion и скриптовых толчков, `Stop` и `Teleport`. Смещение двигает персонажа сквозь стены и склоны так же, как движение, на следующем шаге и не меняет скорость. Поля «Во время игры» показывают скорость, стоит ли персонаж на земле, касается ли стены или потолка, нормаль и угол пола и тело, на котором он стоит.
|
||
|
||
---
|
||
id: gameplay/health
|
||
title: Здоровье и урон
|
||
---
|
||
# Здоровье и урон
|
||
|
||
[«Здоровье»](ref:gameplay.Health) хранит очки здоровья. Хитбоксы, смертельные зоны и скрипты наносят урон через `Damage(amount, source)`, `Kill(source)` и `Heal(amount)`. При нуле узел умирает один раз и остаётся мёртвым до `Revive(hp)`.
|
||
|
||
hp := node.Get<Health>()
|
||
if hp.Damage(2, other) {
|
||
audio.PlayEvent(HitSound)
|
||
}
|
||
|
||
После удара узел игнорирует урон «Время неуязвимости» секунд игрового времени. Отсчёт начинается после шага, поэтому засчитываются все удары одного шага: два одновременно попавших меча ранят оба, а `Died` всё равно срабатывает один раз. `SetInvulnerable(seconds)` намеренно делает узел неуязвимым.
|
||
|
||
При смерти узел с [«Возрождением»](ref:gameplay.Respawn) возвращается. Иначе «Уничтожить при смерти» удаляет его, а «Убрать при смерти» возвращает в его пул. «Команда» защищает узел от хитбоксов той же команды, если в них не включён урон по своим.
|
||
|
||
В сцене нажмите J рядом с манекеном, чтобы ударить его. Он показывает свои очки здоровья и возвращается через секунду после смерти.
|
||
|
||
---
|
||
id: gameplay/hurtbox
|
||
title: Хёртбоксы
|
||
---
|
||
# Хёртбоксы
|
||
|
||
[«Хёртбокс 2D»](ref:gameplay.Hurtbox2D) — область в пространстве своего узла, где хитбоксы ранят «Здоровье»: указанное в его поле «Здоровье» или ближайшее на этом узле или выше. Несколько хёртбоксов могут делить одно «Здоровье», поэтому у персонажа могут быть голова, тело и щит на дочерних узлах.
|
||
|
||
У каждого хёртбокса есть область в пространстве его узла: прямоугольник или вписанный в него круг. **Множитель** масштабирует полученный там урон: 2 для уязвимого места, 0,5 для брони. Выключите **Активен**, чтобы игнорировать удары, — для уклонений и кадров неуязвимости в анимации.
|
||
|
||
У манекена есть хёртбокс головы с «Множителем» 2: подпрыгните и ударьте, чтобы попасть в него.
|
||
|
||
---
|
||
id: gameplay/hitbox
|
||
title: Хитбоксы
|
||
---
|
||
# Хитбоксы
|
||
|
||
[«Хитбокс 2D»](ref:gameplay.Hitbox2D) после шага физики наносит урон хёртбоксам внутри своей области. «Режим ударов» решает, как часто бьётся одна цель:
|
||
|
||
| Режим | Удары |
|
||
|---|---|
|
||
| onEnter | при каждом входе цели |
|
||
| oncePerActivation | один раз на цель до `Activate` |
|
||
| periodic | каждый «Период» секунд, пока области пересекаются |
|
||
|
||
«Команда» и «Урон по своим» не дают атакам задевать свою сторону. «Команда» −1 берёт команду ближайшего «Здоровья» выше, поэтому меч на дочернем узле сражается за владельца и никогда его не ранит. «Отбрасывание» — скорость для цели с «Движением 2D» и импульс для динамического тела. Оно поворачивается вместе с узлом хитбокса, поэтому отражённый персонаж толкает в другую сторону. «Отбрасывание от центра» вместо этого отталкивает от хитбокса.
|
||
|
||
Взмах мечом — один вызов:
|
||
|
||
if input.Pressed("Attack") {
|
||
Sword.Get<Hitbox2D>().Pulse()
|
||
}
|
||
|
||
`Pulse()` наносит один раунд урона на следующем шаге, активен хитбокс или нет. `Activate()` и `Deactivate()` включают хитбокс на время атаки. `Hit(target, damage)` срабатывает для каждой цели, получившей урон.
|
||
|
||
Пройдите по трём шипам в сцене, чтобы сравнить режимы.
|
||
|
||
|
||

|
||
|
||
---
|
||
id: gameplay/lifetime
|
||
title: Время жизни
|
||
---
|
||
# Время жизни
|
||
|
||
[«Время жизни»](ref:gameplay.Lifetime) убирает свой узел через «Секунды» игрового времени или реального с «Реальным временем». С «Убрать» созданный узел возвращается в свой пул — это подходит пулям, искрам и обломкам; иначе узел уничтожается. «Осталось» показывает оставшиеся секунды.
|
||
|
||
Шары в сцене создаются из префаба через `scene.Spawn`, через две секунды возвращаются в пул и снова создаются из него; полоса слева уничтожается через двадцать.
|
||
|
||
---
|
||
id: gameplay/killzone
|
||
title: Смертельные зоны
|
||
---
|
||
# Смертельные зоны
|
||
|
||
[«Смертельная зона 2D»](ref:gameplay.KillZone2D) убивает «Здоровье», позиция узла которого входит в область, или запускает «Возрождение» у узла без «Здоровья». «Тег» ограничивает её узлами с тегом; без тега учитывается любой узел со «Здоровьем» или «Возрождением». Ставьте такие зоны под ямами и одну широкую под уровнем.
|
||
|
||
Зайдите в яму в сцене: персонаж умрёт и вернётся на старт.
|
||
|
||
---
|
||
id: gameplay/checkpoint
|
||
title: Контрольные точки
|
||
---
|
||
# Контрольные точки
|
||
|
||
[«Контрольная точка 2D»](ref:gameplay.Checkpoint2D) переносит точку возрождения вошедшего в неё узла с «Возрождением» в свою позицию или к узлу из «Точки появления». Контрольная точка никогда не заменяет точку с более высоким «Приоритетом», поэтому возврат назад не теряет прогресс. «Один раз» включает её только для первого узла. `Reached(by)` срабатывает, когда узел берёт контрольную точку.
|
||
|
||
---
|
||
id: gameplay/respawn
|
||
title: Возрождение
|
||
---
|
||
# Возрождение
|
||
|
||
[«Возрождение»](ref:gameplay.Respawn) возвращает свой узел после смерти, после смертельной зоны или когда скрипт вызывает `Trigger()`. Первая точка возрождения — место, где узел начинает; контрольные точки и `SetPoint(position)` её переносят. Через «Задержку» секунд узел телепортируется туда, «Восстановить здоровье» оживляет его с полными очками здоровья, и срабатывает `Respawned`.
|
||
|
||
С «Сохранением» точка возрождения хранится в сохранениях, поэтому загруженная игра начинается с последней контрольной точки; «Здоровье» тоже хранит свои очки в сохранениях.
|
||
|
||
Нажмите R в сцене, чтобы возродиться намеренно.
|
||
|
||
---
|
||
id: gameplay/pickup
|
||
title: Подбираемые предметы
|
||
---
|
||
# Подбираемые предметы
|
||
|
||
[«Подбираемый предмет 2D»](ref:gameplay.Pickup2D) посылает `Collected(by)`, когда в него входит узел: узел с тегом или любой узел с «Движением 2D». С «Один раз» предмет после этого исчезает, а с «Убрать» возвращается в свой пул.
|
||
|
||
func Ready() {
|
||
conn = Coin.Get<Pickup2D>().Collected.Connect(OnCoin)
|
||
}
|
||
|
||
func OnCoin(by NodeRef) {
|
||
coins++
|
||
}
|
||
|
||
Счётчик в углу сцены подключён к каждой монете.
|
||
|
||
---
|
||
id: gameplay/statemachine
|
||
title: Машины состояний
|
||
---
|
||
# Машины состояний
|
||
|
||
[«Машина состояний»](ref:gameplay.StateMachine) перечисляет имена состояний, а поведение хранят скрипты её узла. Для состояния `Run` она вызывает, если они есть:
|
||
|
||
| Метод | Когда |
|
||
|---|---|
|
||
| `EnterRun()` | машина входит в Run |
|
||
| `ExitRun()` | машина выходит из Run |
|
||
| `FixedRun(dt float)` | на каждом фиксированном шаге в Run, до того как двигается «Движение 2D» |
|
||
| `UpdateRun(dt float)` | каждый кадр в Run, после Update скриптов |
|
||
|
||
func UpdateIdle(dt float) {
|
||
if node.Get<Motion2D>().Velocity.X != 0 {
|
||
node.Get<StateMachine>().Go("Run")
|
||
}
|
||
}
|
||
|
||
func EnterRun() {
|
||
node.Get<SpriteRenderer>().Tint = RunColor
|
||
}
|
||
|
||
`Go(state)` только ставит смену в очередь. Когда выполняемый метод вернётся, выполнятся Exit старого состояния и Enter нового, а затем сработает `Changed(from, to)` с индексами состояний. `Go` внутри Enter или Exit выполняется после этой смены, поэтому `EnterRun` может сразу отправить машину дальше в `Air`; подряд происходит не больше восьми смен. `Is(state)` и `TimeIn()` читают состояние; «Текущее», «Предыдущее» и «Время» показывают его в Инспекторе.
|
||
|
||
Машина входит в «Начальное» в первом кадре, после Ready скриптов. Методы ищутся по имени и находятся заново после горячей перезагрузки, поэтому переименование метода действует сразу. Методы жизненного цикла, обработчики событий и сопрограммы никогда не считаются методами состояний. «Проблемы» предупреждают о машине без состояний, о состоянии, указанном дважды, и о «Начальном», которого нет среди состояний ([GMP005](ref:GMP005)).
|
||
|
||
Ходите, останавливайтесь и прыгайте в сцене: цвет и надпись следуют за состоянием.
|
||
|
||
---
|
||
id: gameplay/time
|
||
title: Время и повторы
|
||
---
|
||
# Время и повторы
|
||
|
||
Движение, удары, зоны и возрождения работают на фиксированном шаге: `gameplay.fsm_fixed`, а затем `gameplay.motion` — перед физикой, после `script.fixed`; `gameplay.post` — после физики, до того как скрипты получат события столкновений. `gameplay.fsm_update` выполняет методы Update машин состояний после `script.update`. Поэтому сигналы одного шага доходят до скриптов в том же шаге, что и вызвавшие их столкновения.
|
||
|
||
Урон, нанесённый скриптом в Update, применяется сразу. Его `Damaged`, `Healed` и `Died` приходят в конце того же кадра из `gameplay.frame`, который также отсчитывает «Время жизни». Неуязвимость, задержки возрождения и периодические удары считают игровое время и останавливаются на паузе; «Время жизни» с «Реальным временем» считает реальное время.
|
||
|
||
Всё состояние компонентов, включая скрытое состояние контроллера, хитбоксов, зон и машин состояний, хранится в сцене. Перемотка, повторы и пулы восстанавливают его точно, и хеш состояния, который использует `aego test --determinism`, его включает.
|
||
|
||
---
|
||
id: gameplay/scripting
|
||
title: Скрипты
|
||
---
|
||
# Скрипты
|
||
|
||
Компоненты доступны через `node.Get<T>()`; импортировать модуль не нужно.
|
||
|
||
| Компонент | Методы | Сигналы |
|
||
|---|---|---|
|
||
| Motion2D | `Move(x)`, `MoveDir(dir)`, `Jump()`, `JumpReleased()`, `Dash(velocity, seconds)`, `AddVelocity(v)`, `AddDisplacement(offset)`, `Stop()`, `Teleport(position)` | `Landed(speed)`, `LeftGround()`, `Jumped()`, `HitWall(normal)`, `HitCeiling()` |
|
||
| Health | `Damage(amount, source) bool`, `Heal(amount)`, `Kill(source)`, `Revive(hp)`, `SetInvulnerable(seconds)` | `Damaged(amount, source)`, `Healed(amount)`, `Died(source)`, `Revived()` |
|
||
| Hitbox2D | `Activate()`, `Deactivate()`, `Pulse()` | `Hit(target, damage)` |
|
||
| Checkpoint2D | | `Reached(by)` |
|
||
| Respawn | `Trigger()`, `SetPoint(position)` | `Respawned()` |
|
||
| Pickup2D | | `Collected(by)` |
|
||
| StateMachine | `Go(state) bool`, `Is(state) bool`, `TimeIn() float` | `Changed(from, to)` |
|
||
|
||
Тряска при приземлении и экран смерти:
|
||
|
||
func Ready() {
|
||
landed = node.Get<Motion2D>().Landed.Connect(OnLanded)
|
||
died = node.Get<Health>().Died.Connect(OnDied)
|
||
}
|
||
|
||
func OnLanded(speed float) {
|
||
if speed > 8 {
|
||
camera.Shake(0.3)
|
||
}
|
||
}
|
||
|
||
func OnDied(source NodeRef) {
|
||
GameOver.SetEnabled(true)
|
||
}
|
||
|
||
Короткое имя вроде `Health` сначала находит собственный компонент проекта: проект, где уже есть свой `Health`, продолжает получать его, а `node.Get<gameplay.Health>()` обращается к этому компоненту по полному имени.
|
||
|
||
---
|
||
id: gameplay/editor
|
||
title: Инструменты редактора
|
||
---
|
||
# Инструменты редактора
|
||
|
||
Редактор собирает готовые узлы геймплея, рисует области ударов и во время игры показывает состояние контроллеров, здоровья и машин состояний.
|
||
|
||
## Готовые узлы
|
||
|
||
**Создать → Геймплей** собирает готовые узлы:
|
||
|
||
- «Персонаж платформера»: кинематическое тело, коллайдер, «Движение 2D», «Здоровье», «Хёртбокс 2D» и «Возрождение»;
|
||
- «Персонаж с видом сверху»;
|
||
- «Враг»: динамическое тело, «Здоровье» в команде 1 и периодический «Хитбокс 2D» с отбрасыванием;
|
||
- «Движущаяся платформа», «Контрольная точка», «Смертельная зона» и «Подбираемый предмет».
|
||
|
||
## Обязательные компоненты
|
||
|
||
**Добавить компонент** добавляет то, что требует компонент. «Движение 2D» на пустом узле приносит с собой «Тело 2D», и одна отмена убирает оба. Список показывает, что добавится вместе с ним. `aego scene add-comp`, вставка и пресеты добавляют их так же. Если добавить «Движение 2D» на узел с телом, тело станет кинематическим.
|
||
|
||

|
||
|
||
## Области
|
||
|
||
- Выберите узел с хитбоксом, хёртбоксом, смертельной зоной, контрольной точкой или подбираемым предметом, чтобы перетаскивать углы и края его области. Привязка работает, и каждое перетаскивание — один шаг отмены. Хитбокс ещё показывает своё отбрасывание стрелкой.
|
||
- **Геймплей → Области геймплея** показывает все области приглушённо на панели «Сцена». Во время игры панель «Игра» рисует активные хёртбоксы и хитбоксы, зоны и полоску здоровья над каждым узлом со «Здоровьем».
|
||
|
||
## Инспектор во время игры
|
||
|
||
Состояние контроллера во время игры показывает его скорость, стоит ли он на земле, касание стены и потолка, пол и тело под ним. «Движение 2D» добавляет сводку контактов, «Здоровье» — полоску с очками здоровья, а «Машина состояний» — текущее состояние и сколько оно длится. «Время жизни» показывает оставшиеся секунды.
|
||
|
||
## Профилирование и проблемы
|
||
|
||
- **Профилировщик → Геймплей** показывает контроллеры и проверки за фиксированный шаг, а также удары, смерти, возрождения и подборы на текущий момент.
|
||
- «Монитор событий» перечисляет смены состояний, урон (с величиной до 9), смерти, возрождения и подборы строками `gameplay`.
|
||
- «Проблемы» показывают «Движение 2D», тело которого не кинематическое ([GMP001](ref:GMP001), блокирует игру), платформер с «Масштабом гравитации» 0 без «Скорости прыжка» ([GMP002](ref:GMP002)), машину состояний с пустыми, повторяющимися или неизвестными состояниями ([GMP005](ref:GMP005)) и «Хёртбокс 2D» без «Здоровья» на своём узле или выше ([GMP006](ref:GMP006); удары по нему игнорируются).
|
||
|
||
---
|
||
id: gameplay/go
|
||
title: Go
|
||
---
|
||
# Go
|
||
|
||
if hp, ok := scene.Get[gameplay.Health](s, node); ok {
|
||
hp.Damage(3, attacker)
|
||
}
|
||
if m, ok := scene.Get[gameplay.Motion2D](s, player); ok {
|
||
m.AddVelocity(mathx.Vec2{X: 6, Y: -4})
|
||
}
|
||
st := gameplay.Of(world).Stats()
|
||
|
||
Типы компонентов и их методы те же, что в скриптах. `gameplay.Of(world)` возвращает сервис мира игры; его метод `Stats` считает удары, смерти, возрождения и подборы с начала игры.
|
||
|
||
Машины состояний вызывают методы скриптов через контракт `script.call` (`contracts/scriptcall`): `Method(node, name, params)` находит обычный метод скрипта на узле, `Call` выполняет его, а новый `Generation` после горячей перезагрузки делает старые ссылки устаревшими. Раздел сохранения `gameplay.respawn` хранит точки возрождения с отметкой «Сохранение».
|
||
|
||
«Движение 2D» читает и двигает тела только через контракт `physics2d.queries`: `CastBody` из сдвинутой позы, `Penetration`, `BodyPose`, `BodyVelocity`, `MoveKinematic`, `Teleport` и `Gravity`. С любым физическим модулем, который предоставляет этот контракт, контроллер работает без изменений. Root motion доходит до контроллера через контракт `motion2d.receiver` (`contracts/motion2d`): `AddDisplacement(node, offset)` возвращает false для узла без «Движения 2D».
|