# 13. Звуковые эффекты

Table of Contents

13. Звуковые эффекты

Цель

Сделать игру отзывчивой на слух: игрок слышит отклик интерфейса, появление новых шаров, перемещение, снятие линии и окончание партии.

Описание выполненной работы

Зачем отдельные шины и автозагрузчик

В Godot 4 громкость удобнее крутить не на каждом узле по отдельности, а на аудиошине (AudioBus). У проекта уже были шины Music и SFX, созданные в GameAudio._ready(), и слайдеры на титульном экране, которые через GameAudio.set_bus_linear() записывают уровень в user://game_settings.cfg. Шаг 13 добавляет к этой схеме конкретные потоки и политику проигрывания, чтобы любая сцена могла вызвать один метод и не заботиться о создании AudioStreamPlayer.

Автозагрузчик (singleton) живёт дольше любой сцены: если создавать AudioStreamPlayer только на игровом поле, то при возврате в меню узлы уничтожаются и логику проигрывания пришлось бы дублировать. Поэтому пул коротких плееров и точки входа play_* сосредоточены в game_audio.gd.

Файлы в src/assets/audio

Чтобы не тащить в репозиторий чужие записи с неясной лицензией, звуки сгенерированы скриптом tools/gen_audio_assets.py (чистый Python, модуль wave): синусоиды с огибающими и простые арпеджио. Это «заглушки», которые позже можно заменить на записи художника без изменения кода — достаточно подставить файлы с теми же именами и обновить импорт в редакторе Godot.

Имена файлов зафиксированы в game_audio.gd через preload:

Константа в кодеФайлНазначение в игре
STREAM_UIsfx_ui.wavКороткий щелчок по кнопкам меню и выбор шара на поле
STREAM_PLACEsfx_place.wavПосле выкладки трёх новых шаров в начале хода человека или ИИ
STREAM_MOVEsfx_move.wavВ момент завершения перелёта шара по клеткам
STREAM_MATCHsfx_match.wavВ начале анимации снятия линии из трёх и более шаров
STREAM_GAME_OVERsfx_game_over.wavПеред показом оверлея конца партии

После первого открытия проекта Godot создаст для каждого .wav файл .import и положит данные в .godot/imported/. Если звук не слышен, первым делом проверьте, что ресурс действительно импортировался (нет ошибок в консоли редактора) и что шина SFX не уведена в −80 dB ползунком «Эффекты».

Пул AudioStreamPlayer для SFX

Один AudioStreamPlayer может воспроизводить только один поток одновременно. Если игрок быстро жмёт кнопки или цепочка ходов даёт несколько событий подряд, второй звук «проглотит» первый. Поэтому в GameAudio._setup_sfx_pool() создаётся несколько дочерних AudioStreamPlayer (по умолчанию пять), все с bus = "SFX". Вспомогательная функция _play_sfx(stream) ищет свободный плеер; если все заняты, останавливает первый и переиспользует его — для мобильной казуальной игры этого достаточно.

Если позже понадобится приоритет (например, конец партии всегда пробивается через ход), можно завести отдельный зарезервированный плеер или очередь — архитектура с методами play_* позволяет поменять только автозагрузчик.

Где вызываются звуки в логике поля

Файл game_board.gd уже содержит чёткие точки жизненного цикла хода; туда добавлены однострочные вызовы GameAudio, чтобы не смешивать логику матчинга и аудио:

  1. _start_human_turn() и _start_ai_turn() после _sync_cells() и обновления превью очереди вызывают GameAudio.play_place_batch(). Так мы не воспроизводим звук при загрузке сохранённой партии посередине хода — восстановление состояния не проходит через эти функции с новой случайной выкладкой в том же виде, как свежий ход.

  2. _animate_move_then_apply() вызывает GameAudio.play_ball_move() во всех ветках, где вызывается _board.apply_move(): и после поэтапного твина по пути, и в коротком пути path.size() < 2, и при отсутствии CellView (защитная ветка). Так игрок всегда слышит «приземление», когда состояние доски меняется после перемещения.

  3. _animate_matched_cells_pop() сразу после проверки непустого словаря вызывает GameAudio.play_match_clear(), чтобы звук совпал с началом анимации сжатия и прозрачности.

  4. _offer_game_over() в самом начале, до установки флагов, вызывает GameAudio.play_game_over_stinger(). Один звук на партию гарантируется тем, что функция повторно выходит, если _game_ended уже true.

  5. Выбор своего шара на поле (_on_cell_clicked()) даёт GameAudio.play_board_select() при первичном выборе и при переключении на другой свой шар — пустые клетки по-прежнему не дают звука, чтобы не шуметь при промахах.

Кнопки «Назад» и «Меню» на экране результата вызывают GameAudio.play_ui_click() перед сменой сцены на main.tscn.

Интерфейс титульного экрана и два оверлея

На title_screen.gd звук щелчка добавлен к действиям Играть, Новая игра, Звук, Правила, Выход, Закрыть у справки и настроек. Чтобы не получить два щелчка подряд при переходе между панелями «Звук» и «Правила», вспомогательные функции _close_help(play_click) и _close_settings(play_click) принимают флаг: при программном закрытии одной панели при открытии другой play_click передаётся как false, а пользовательский клик по «Закрыть» оставляет звук включённым.

Слайдеры громкости не озвучиваются на каждый шаг — это создало бы шум при перетаскивании.

Генерация и замена ассетов

Скрипт tools/gen_audio_assets.py можно запускать так из корня репозитория:

Terminal window
python tools/gen_audio_assets.py

Он перезапишет WAV в src/assets/audio/. Параметры (частоты, длительность, громкость) лежат в функциях sine_burst, sfx_move, sfx_match и т.д.; имеет смысл экспериментировать на ПК, затем один раз импортировать в Godot и прослушать на телефоне с наушниками и без — на Android иногда заметна большая задержка вывода (buffer), её частично смягчает driver/output_latency_ms в project.godot.

Если вы заменяете файл своей записью в формате WAV или OGG, не забудьте поменять тип preload в game_audio.gd для музыки (шаг 14) или использовать ResourceLoader.load с проверкой типа — для SFX достаточно оставить WAV и те же имена путей.

Расширение набора эффектов

Добавление нового звука сводится к трём шагам: положить файл в assets/audio/, добавить const STREAM_* := preload(...) рядом с остальными, объявить func play_my_event() -> void: с _play_sfx(STREAM_*) и вызвать этот метод из нужной игровой функции. Так game_board не знает путей к ресурсам и остаётся читаемым.

Контрольный список проверки на устройстве

Убедитесь, что медиафокус не заглушает приложение (другой плеер с высоким приоритетом). Проверьте ползунок «Эффекты» на титуле и сохранение user://game_settings.cfg после выхода из игры. Пройдите короткую партию: выкладка → выбор шара → ход без линии → ход со снятием линии → насильственный конец поля без ходов — на каждом этапе должен быть ожидаемый звук или его сознательное отсутствие по правилам выше.

Ссылка на проект: Third Planet

Ссылка на игру: Google Play Rustore

Next: 14. Музыка
Аватар автора

Спасибо, что прочитали статью. Посмотрите другие материалы в архиве, там много практических разборов по разным технологиям.


godot Series

# 16. Тени

godot 16 / 19
4 min read

Тени через StyleBoxFlat у ячеек, HUD, слота баннера и карточки результата; стили кнопок на титуле и поле; лёгкий контактный затемнённый слой на шарах-планетах.

Read