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
253 lines
23 KiB
Markdown
253 lines
23 KiB
Markdown
---
|
||
id: audio
|
||
group: audio
|
||
group_title: Звук
|
||
title: Звук
|
||
---
|
||
# Звук
|
||
|
||
Звук в Aego — это импортированные клипы. Их играют компоненты, скрипты и звуковые события, а микшируются они на шинах, громкость которых игрок задаёт в настройках. Звук относится к представлению: он никогда не меняет состояние игры, а перемотка и повтор проходят беззвучно.
|
||
|
||
Быстрый старт: перетащите `.wav` в проект, добавьте узлу [«Источник звука 2D»](ref:audio.AudioSource2D), задайте «Клип» и «Запуск при старте» и нажмите «Играть».
|
||
|
||
| Часть | Назначение |
|
||
|---|---|
|
||
| Клип | импортированный файл `.wav`, `.ogg` или `.mp3` |
|
||
| «Источник звука 2D» | играет клип или звуковое событие из своего узла |
|
||
| «Слушатель звука 2D» | уши пространственного звука |
|
||
| «Звуковая зона 2D» | делает шину тише или громче, пока слушатель внутри |
|
||
| Звуковое событие | ассет данных: варианты, случайность, предел голосов и пространство |
|
||
| [aego/audio](ref:aego/audio) | API для скриптов |
|
||
|
||
Звук работает во время игры и в `aego run`. В режиме редактирования «Инспектор» даёт прослушать клипы, а панель «Сцена» показывает дальность пространственных источников и зоны.
|
||
|
||
В «Попробовать» шар гудит, пока катится от стены к стене, и щёлкает, когда ударяется о неё. Слушатель находится на камере, поэтому гул смещается по панораме и затихает, когда шар движется. Отметьте **Replay** у скрипта, чтобы гул зазвучал снова. Браузер включает звук после первого щелчка по странице.
|
||
|
||
---
|
||
id: audio/clips
|
||
title: Клипы
|
||
---
|
||
# Клипы
|
||
|
||
`.wav` (8-, 16-, 24- и 32-битный PCM, 32-битный float) и `.ogg` Vorbis, моно или стерео, импортируются как есть. `.mp3` при импорте переводится в `.ogg` с помощью `ffmpeg`. Если его нет в `PATH`, импорт завершается ошибкой [AUD001](ref:AUD001) с командой, которой файл можно перевести вручную.
|
||
|
||
Настройки импорта в `.meta`:
|
||
|
||
| Настройка | По умолчанию | Значение |
|
||
|---|---|---|
|
||
| `stream` | `auto` | `auto` потоково играет файлы длиннее 2 МБ или 10 секунд; `on` и `off` задают это принудительно |
|
||
| `loopStart`, `loopEnd` | `-1` | точки повтора в кадрах файла; первые значения берутся из блока `smpl` в wav |
|
||
| `gain` | `1` | громкость клипа до источника и шины |
|
||
| `bus` | `sfx` | шина, если тот, кто играет клип, не выбрал её сам |
|
||
|
||
Клип без потоковой загрузки декодируется один раз, с частотой вывода, и общий для всех голосов, которые его играют. Потоковый клип декодируется во время воспроизведения из ассета в памяти; это выбор для музыки. Точки повтора за пределами звука дают [AUD002](ref:AUD002), и тогда повторяется весь звук.
|
||
|
||
Панель «Ассеты» рисует форму волны каждого клипа.
|
||
|
||

|
||
|
||
## Встроенные клипы
|
||
|
||
Вместе с движком поставляются четыре клипа, которым не нужны файлы: **Beep**, **Click**, **Hum** и **Melody**. Они есть в каждом списке выбора клипа в `engine/audio`, а скрипты получают их через `audio.Builtin("Beep")`. У Hum и Melody точки повтора охватывают весь клип, поэтому они повторяются без шва. Их играют сцены «Попробовать» на этих страницах.
|
||
|
||
---
|
||
id: audio/events
|
||
title: Звуковые события
|
||
---
|
||
# Звуковые события
|
||
|
||
Звуковое событие — это [ассет данных](ref:data) типа Audio Event (**Создать → Ассет данных → Audio Event**). Оно называет один или несколько клипов и то, как их играть:
|
||
|
||
| Поле | Значение |
|
||
|---|---|
|
||
| `Variants` | клипы; при каждом воспроизведении один выбирается случайно |
|
||
| `Volume`, `Volume Random` | громкость и насколько тише может прозвучать каждое воспроизведение |
|
||
| `Pitch`, `Pitch Random` | высота тона и насколько вверх или вниз она может уйти при каждом воспроизведении, чтобы повторы не звучали одинаково |
|
||
| `Bus`, `Priority` | шина и то, какие голоса микшер вытесняет первыми, когда голосов не хватает |
|
||
| `Max Voices`, `Steal` | на пределе новое воспроизведение останавливает самый старый или самый тихий голос события либо пропускается |
|
||
| `Cooldown` | секунды игрового времени до следующего воспроизведения события |
|
||
| `Spatial` … `Pan Width` | см. [Пространственный звук](ref:audio/spatial) |
|
||
| `Loop`, `Pause With` | повтор до остановки; `game` ставится на паузу вместе с миром, `never` продолжает играть |
|
||
|
||
Скрипт играет событие через `audio.PlayEvent(Jump)` из `@export var Jump AssetRef<AudioEvent>`. «Источник звука 2D» играет событие, если задано его поле «Событие», и тогда настройки события заменяют поля воспроизведения источника. Пределы голосов и паузы между воспроизведениями считаются для каждого мира отдельно. Случайный выбор берётся из собственного зерна звукового сервиса, поэтому повтор выбирает те же варианты, а звук никогда не затрагивает случайные числа игры.
|
||
|
||
Как и любой ассет данных, событие может иметь базу и переопределять несколько полей. Событие без вариантов или пространственное событие, у которого Max Distance не больше Min Distance, — ошибка ([AUD006](ref:AUD006)).
|
||
|
||
---
|
||
id: audio/spatial
|
||
title: Пространственный звук
|
||
---
|
||
# Пространственный звук
|
||
|
||
Пространственный звук затихает и смещается по панораме в зависимости от расстояния до слушателя. Слушатель — первый включённый активный [«Слушатель звука 2D»](ref:audio.AudioListener2D) в иерархии; другой активный слушатель ниже в иерархии не учитывается ([AUD005](ref:AUD005)). Без слушателя слушает камера главного вывода, и [AUD004](ref:AUD004) напоминает об этом на пространственных источниках; без камеры слушает начало координат.
|
||
|
||
`audio.ListenerPosition()` возвращает последнюю вычисленную позицию слушателя в мировых координатах во время игры, включая запасной вариант с камерой.
|
||
|
||
Пространственный источник играет на полной громкости до **«Мин. расстояния»** и молчит начиная с **«Макс. расстояния»**; оба задаются в единицах мира. **«Затухание»** — кривая громкости между ними: время 0 — это «Мин. расстояние», время 1 — «Макс. расстояние». **«Ширина панорамы»**, от 0 до 1, задаёт, насколько смещается по панораме звук, который находится сбоку на «Макс. расстоянии».
|
||
|
||
Все поля перечислены на странице [«Источник звука 2D»](ref:audio.AudioSource2D).
|
||
|
||
Кривая «Затухания» с ключами читается как громкость; пресет плавности читается как спад громкости; кривая по умолчанию падает до четверти на середине. Панорама — это расстояние вбок, делённое на «Макс. расстояние» и умноженное на «Ширину панорамы».
|
||
|
||
Звук, запущенный из узла, следует за ним; `audio.PlayAt` и `audio.PlayEventAt` остаются там, где начались. Позиции читаются после движения в кадре, поэтому звук следует за тем, что нарисовано.
|
||
|
||
В «Попробовать» слушатель находится на шаре, а камера следит за шаром. Ящик в середине гудит: он звучит громко и по центру, когда шар проходит мимо, и затихает в стороне, когда шар откатывается. Измените «Макс. расстояние» или «Ширину панорамы» ящика в «Инспекторе» и послушайте следующий проход.
|
||
|
||
---
|
||
id: audio/zones
|
||
title: Зоны
|
||
---
|
||
# Зоны
|
||
|
||
Пока слушатель находится внутри [«Области»](ref:audio.AudioZone2D.Area) [«Звуковой зоны 2D»](ref:audio.AudioZone2D), «Шина» зоны за «Время перехода» приводится к её «Уровню». Область — прямоугольник в пространстве узла зоны.
|
||
|
||
`audio.WorldBusGain(bus)` возвращает последнее целевое значение слоя мира, выбранное зонами. Настоящий микшер также применяет плавный переход, громкость сеанса и настройки игрока.
|
||
|
||
- Среди пересекающихся зон на одной шине побеждает зона с наибольшим «Приоритетом», а при равенстве — та, в которую вошли позже. Выход из одной зоны оставляет другие на месте.
|
||
- Зоны действуют на слой мира в микшере, под настройками игрока и сеансом, и заканчиваются вместе с миром.
|
||
- Зона меняет всю шину, а не один источник: используйте её, чтобы приглушить музыку в помещении или сделать тише фоновые звуки рядом с меню.
|
||
|
||
В «Попробовать» шина музыки падает до пятой части, пока слушающий шар катится через фиолетовую область, и возвращается, когда он её покидает. Задайте «Уровень» 0, чтобы музыка внутри смолкла, или увеличьте «Время перехода» для более медленной смены.
|
||
|
||
---
|
||
id: audio/buses
|
||
title: Шины и настройки
|
||
---
|
||
# Шины и настройки
|
||
|
||
Каждый звук играет на шине: Music, Effects, Interface, Ambience или Voice, все под Master. Громкость шины — произведение трёх слоёв:
|
||
|
||
1. настройки игрока, `audio.bus.<name>.volume` и `mute`, на странице «Звук» в [Настройках](ref:settings); проект задаёт их значения по умолчанию в `project.aego`;
|
||
2. сеанс: `audio.SetBusVolume` и `audio.Mute` в скриптах и регуляторы «Аудиомикшера»; сеанс никогда не сохраняется;
|
||
3. мир: звуковые зоны.
|
||
|
||
Каждое изменение идёт плавно, поэтому ничего не щёлкает. `audio.enabled` выключает весь звук, не закрывая вывод; `audio.bufferSize` и `audio.sampleRate` открывают вывод заново, а играющие звуки продолжаются.
|
||
|
||
---
|
||
id: audio/music
|
||
title: Музыка, пауза и перемотка
|
||
---
|
||
# Музыка, пауза и перемотка
|
||
|
||
`audio.Music(Theme, 1.5)` повторяет клип на шине музыки и плавно переходит с музыки, которая играла до него; `audio.StopMusic(1)` плавно её убирает. Музыка продолжает играть, пока игра на паузе.
|
||
|
||
Пауза мира ставит на паузу звуки, у которых «Пауза вместе с» равно `game`, и не трогает звуки с `never`; `PlayOptions.Unscaled` делает то же для одного воспроизведения. Звук останавливается с коротким затуханием, когда узел, который его запустил, уничтожается.
|
||
|
||
Перемотка и повтор беззвучны: звуки, запущенные до перехода по таймлайну, сразу останавливаются, во время повтора записи ничего не играет, а после возврата в Live новые звуки снова играют. Звуки не входят в хеш состояния, поэтому игра воспроизводится одинаково со звуком и без него. Ждите игровую логику таймером, а не звуком.
|
||
|
||
---
|
||
id: audio/scripting
|
||
title: Скрипты
|
||
---
|
||
# Скрипты
|
||
|
||
import "aego/audio"
|
||
|
||
@export var Jump AssetRef<AudioEvent>
|
||
@export var Theme AssetRef<Clip>
|
||
|
||
func Ready() {
|
||
audio.Music(Theme, 1.5)
|
||
}
|
||
|
||
func OnJump() {
|
||
audio.PlayEvent(Jump)
|
||
}
|
||
|
||
| Функция | |
|
||
|---|---|
|
||
| `Play(clip) Sound`, `PlayWith(clip, options) Sound` | играет клип на его шине или с `PlayOptions` |
|
||
| `PlayAt(clip, position) Sound` | играет клип в точке мира, пространственно |
|
||
| `PlayEvent(event) Sound`, `PlayEventAt(event, position) Sound` | играет звуковое событие; пространственное событие следует за узлом скрипта |
|
||
| `Music(clip, seconds) Sound`, `StopMusic(seconds)` | повторяет музыку с плавным переходом, плавно убирает её |
|
||
| `BusVolume(bus) float`, `SetBusVolume(bus, volume)`, `Mute(bus, on)` | громкость шины на сеанс поверх настроек игрока |
|
||
| `Pause(on)` | ставит на паузу или продолжает все звуки этого мира |
|
||
| `Builtin(name) AssetRef<Clip>` | встроенный клип: Beep, Click, Hum или Melody |
|
||
|
||
У `PlayOptions` есть Volume, Pitch, Pan, Bus, Loop, FadeIn, Priority и Unscaled; Volume и Pitch, равные 0, означают 1, а Bus Master играет на шине клипа. У `Sound` есть `Done()`, `Playing()`, `Stop()`, `StopFade(seconds)`, `Pause()`, `Resume()`, `SetVolume`, `SetPitch`, `SetPan`, `Position()` и сигнал `Completed(finished bool)`; `yield` ждёт его, а nil-звук считается завершённым. Каждое воспроизведение принадлежит узлу скрипта и останавливается, когда узел уничтожается.
|
||
|
||
Методы компонента: `AudioSource2D.Play()`, `Stop()`, `StopFade(seconds)` и `IsPlaying()`, а также сигналы `Started` и `Finished(finished bool)`.
|
||
|
||
```aes run
|
||
package demo
|
||
|
||
import (
|
||
"aego/audio"
|
||
"aego/log"
|
||
)
|
||
|
||
script Chime {
|
||
var hum audio.Sound
|
||
var frames int
|
||
|
||
func Ready() {
|
||
hum = audio.PlayWith(audio.Builtin("Hum"), audio.PlayOptions{Volume: 0.5, Pitch: 1.5, Loop: true})
|
||
hum.Completed.Connect(Ended)
|
||
log.Info("the hum plays: " + string(hum.Playing()))
|
||
}
|
||
|
||
func Ended(finished bool) {
|
||
log.Info("the hum ended, finished " + string(finished))
|
||
}
|
||
|
||
func Update(dt float) {
|
||
frames++
|
||
if frames == 60 {
|
||
hum.StopFade(0.2)
|
||
log.Info("fading at " + string(hum.Position()) + " s")
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
id: audio/editor
|
||
title: Инструменты редактора
|
||
---
|
||
# Инструменты редактора
|
||
|
||
Редактор микширует, прослушивает и рисует звук: микшер для шин, кнопки прослушивания на источниках и дальность пространственных звуков в панелях.
|
||
|
||
## Аудиомикшер
|
||
|
||
**Окно → Аудиомикшер** или раскладка «Звук» показывает для каждой шины индикатор уровня, регулятор, кнопки mute и solo, а также играющие голоса и опустошения буфера. Регуляторы меняют текущий сеанс. **Сбросить сеанс** возвращает их назад; **Сохранить в профиль** записывает их в настройки игрока.
|
||
|
||

|
||
|
||
## Инспектор
|
||
|
||
В режиме редактирования у «Источника звука 2D» есть **Прослушать** и **Остановить** для его клипа и полоса прогресса. Во время игры у него есть **Воспроизвести** и **Остановить**, и он показывает, сколько звука уже прошло.
|
||
|
||
## Ручки и дальность
|
||
|
||
- Выберите пространственный источник, чтобы перетаскивать «Мин. расстояние» и «Макс. расстояние», или зону, чтобы перетаскивать её область. Каждое перетаскивание — один шаг отмены.
|
||
- **Звук → Радиусы звука** включает и выключает круги и зоны на панели «Сцена». Во время игры панель «Игра» также рисует слушателя, играющие пространственные голоса и линии к ним.
|
||
|
||
## События, профилирование и проблемы
|
||
|
||
- «Монитор событий» показывает каждое воспроизведение и остановку строками `audio`. В «Профилировщике» есть раздел «Звук»: вывод, голоса, время микширования и опустошения буфера.
|
||
- «Проблемы» перечисляют источники без клипа и без события ([AUD003](ref:AUD003)), пространственные источники без слушателя ([AUD004](ref:AUD004)), лишних слушателей ([AUD005](ref:AUD005)), сломанные события ([AUD006](ref:AUD006)), нечитаемые файлы ([AUD001](ref:AUD001)) и точки повтора за пределами звука ([AUD002](ref:AUD002)).
|
||
|
||
---
|
||
id: audio/web
|
||
title: Звук в вебе
|
||
---
|
||
# Звук в вебе
|
||
|
||
Браузеры включают звук только после щелчка, нажатия клавиши или касания на странице; страница, открытая щелчком, например «Попробовать» на этом сайте, играет сразу. Веб-экспорт показывает в углу кнопку отключения звука. Она заглушает все шины на выходе, и браузер запоминает выбор до первого кадра следующего посещения. Если страница отдаётся с `Cross-Origin-Opener-Policy: same-origin` и `Cross-Origin-Embedder-Policy: require-corp`, задержка вывода на один блок меньше. Потоковые клипы читаются из скачанных данных, а не по сети.
|
||
|
||
---
|
||
id: audio/go
|
||
title: Go
|
||
---
|
||
# Go
|
||
|
||
snd := sdk.AudioOf(ctx)
|
||
beep, _ := sdk.BuiltinClip("Beep")
|
||
s := snd.PlayAt(beep.ID, pos, sdk.AudioOptions{Volume: 0.8}, node)
|
||
if snd.Done(s) { ... }
|
||
|
||
У `Service` есть `Play`, `PlayAt`, `PlayEvent`, `PlayEventAt`, `Trigger` и `TriggerAt` для значения события, построенного в Go, а также `Music`, `StopMusic`, `Stop`, `Pause`, `SetVolume`, `SetPitch`, `SetPan`, `Done`, `Playing`, `Position` и `PauseAll`. У каждого игрового мира свой сервис. Модуль, который находится через `audio.ModuleOf(engine)`, владеет микшером, громкостями сеанса и кэшем клипов.
|
||
|
||
Система `audio.ready` (PreUpdate) запускает источники с «Запуском при старте» в первом кадре; `audio.world` (PostUpdate, работает и на паузе) двигает пространственные голоса, следует за слушателем и применяет зоны. Микшер работает в своём потоке и получает команды через кольцевой буфер, поэтому игровой поток никогда его не ждёт.
|