reckfellENGINE

Шины: музыка, эффекты и настройки громкости

Рабочие ползунки категорий, линейный коэффициент и дБ, mute, маршрутизация и сохранение layout.

На этой странице

Разделите категории

Создайте Music, SFX и UI, направленные в Master. Назначайте AudioStreamPlayer.Bus по категории, а не регулируйте все плееры обходом дерева. Шины существуют для всего проекта. Их индекс меняется при вставке, удалении или перестановке; храните имя и заново вызывайте AudioServer.GetBusIndex(name) перед изменением. Для отсутствующего имени возвращается -1 — не передавайте его в методы настройки.

Плеер с несуществующим именем шины использует Master; это может скрыть опечатку. Проверяйте имена при подготовке проекта. Принимающая шина должна находиться левее отправляющей. Попытка отправить сигнал вправо или в отсутствующее назначение фактически возвращает маршрут в Master; это не способ построить цикл.

Линейный коэффициент и дБ

Линейная амплитудаИзменение уровня
10 dB: усиление не меняется
0.5примерно -6.02 dB
0.1-20 dB
0Тишина: используйте mute, не вычисляйте логарифм нуля

Для отношения амплитуд dB = 20 × log10(gain). Это не формула мощности и не шкала субъективной громкости: половина амплитуды не гарантирует ощущения «вдвое тише». 0 dB на фейдере означает единичный коэффициент, а 0 dBFS у цифрового пика — полную шкалу сигнала. Это разные величины. Микшер может суммировать значения выше полной шкалы; избегайте перегрузки конечного выхода. Запас уровня и лимитер не заменяют разумный баланс источников.

Запустить готовое меню

Сохраните DemoTone.cs из примера плеера в проекте. Добавьте AudioMix.cs и MixerDemo.cs ниже. Создайте уровень с корнем Control, прикрепите MixerDemo, соберите и запустите F6. Тон играет через Music; кнопка проверяет UI. Ползунок Music не меняет UI, ползунок UI не меняет Music. Ползунок SFX готов для ваших эффектов, в этом примере к нему звук не подключён.

AudioMix.cs
C#
using System;
using Reckfell;

public static class AudioMix
{
    public static int EnsureBus(string name)
    {
        if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("A bus needs a name.", nameof(name));
        int existing = AudioServer.GetBusIndex(name);
        if (existing >= 0) return existing;
        AudioServer.AddBus();
        int index = AudioServer.BusCount - 1;
        AudioServer.SetBusName(index, name);
        AudioServer.SetBusSend(index, "Master");
        return index;
    }

    public static bool SetGain(string name, float linear)
    {
        if (!float.IsFinite(linear)) return false;
        int index = AudioServer.GetBusIndex(name);
        if (index < 0) return false;
        linear = Mathf.Clamp(linear, 0, 1);
        AudioServer.SetBusMute(index, linear == 0);
        if (linear > 0) AudioServer.SetBusVolumeDb(index, Mathf.LinearToDb(linear));
        return true;
    }

    public static float GetGain(string name)
    {
        int index = AudioServer.GetBusIndex(name);
        return index < 0 || AudioServer.IsBusMute(index) ? 0 : AudioServer.GetBusVolumeLinear(index);
    }
}
MixerDemo.cs
C#
using System;
using Reckfell;

public partial class MixerDemo : Control
{
    public AudioStreamPlayer Music { get; private set; } = null!;
    public AudioStreamPlayer Click { get; private set; } = null!;
    private bool _ru;

    public override void _Ready()
    {
        _ru = TranslationServer.GetLocale().StartsWith("ru", StringComparison.OrdinalIgnoreCase);
        foreach (string name in new[] { "Music", "SFX", "UI" }) AudioMix.EnsureBus(name);
        Music = new AudioStreamPlayer { Name = "Music", Bus = "Music", Stream = DemoTone.Create(220, loop: true), PlaybackType = AudioServer.PlaybackType.Stream };
        Click = new AudioStreamPlayer { Name = "Click", Bus = "UI", Stream = DemoTone.Create(880, .08), PlaybackType = AudioServer.PlaybackType.Stream };
        AddChild(Music); AddChild(Click);
        var layout = new VBoxContainer { Position = new Vector2(24, 24), CustomMinimumSize = new Vector2(280, 0) }; AddChild(layout);
        AddSlider(layout, "Music", _ru ? "Музыка" : "Music");
        AddSlider(layout, "SFX", _ru ? "Эффекты" : "Effects");
        AddSlider(layout, "UI", _ru ? "Интерфейс" : "Interface");
        var click = new Button { Text = _ru ? "Проверить UI" : "Test UI" }; layout.AddChild(click);
        click.Pressed += () => Click.Play(); Music.Play();
    }

    private static void AddSlider(VBoxContainer layout, string bus, string text)
    {
        layout.AddChild(new Label { Text = text });
        var slider = new HSlider { MinValue = 0, MaxValue = 1, Step = .01, Value = AudioMix.GetGain(bus) };
        layout.AddChild(slider); slider.ValueChanged += value => AudioMix.SetGain(bus, (float)value);
    }
}

EnsureBus создаёт только отсутствующую шину; существующие эффекты, маршрут и громкость не сбрасывает. SetGain отклоняет NaN/Infinity, проверяет имя и ограничивает свой ползунок диапазоном 0…1. Ноль включает mute, ненулевое значение снимает mute и задаёт коэффициент. Это политика данного меню, а не ограничение API движка: сам микшер допускает усиление выше единицы. Если у вас отдельная кнопка «без звука», храните её состояние отдельно, чтобы движение ползунка случайно не снимало пользовательский mute.

Mute не останавливает музыку: позиция продолжает идти, и после снятия mute слышна текущая часть. Для настоящей паузы используйте StreamPaused у плеера. Solo — инструмент изоляции шины при диагностике, а не настройка громкости категории.

Сохранить схему микшера и настройки игрока

В нижней панели Audio создайте шины, задайте Send и добавьте эффекты через Add Effect. Для новой схемы используйте Save Audio Bus Layout As.... Стандартная схема проекта хранится в asset://default_bus_layout.rdata; выбранная схема задаётся audio/buses/default_bus_layout. Изменения AudioServer во время игры меняют память текущего процесса и сами по себе не записывают проектную схему.

Сохраняйте предпочтения игрока по стабильным именам категорий и значениям ползунков в user:// через ConfigFile. Загружайте их после подготовки шин, проверяйте тип и конечность чисел. Не сохраняйте индексы шин и не перезаписывайте ресурс проекта пользовательскими настройками. При переименовании категории явно перенесите старый ключ.

Если маршрут работает неправильно

СимптомПроверка
Ползунок управляет не той категориейСтарый индекс после перестройки layout; ищите по имени
Ничего не меняетсяBus плеера, опечатка имени, принимающая шина и Master
Музыка возвращается с другой позицииMute сохраняет ход времени; это ожидаемо
В захвате есть звук при mute той же шиныЭффекты выполняются до фейдера/mute этой шины; захватывайте на принимающей шине для учёта уровня дочерней
После перезапуска настройки пропалиRuntime-изменения не сохраняются автоматически
Документация Reckfell EngineНаверх ↑
БЫСТРЫЙ ПЕРЕХОДНачало работыEntity и иерархияC# & .NET 10Сцены и префабы
Руководства и полный API Reckfell K
reckfellENGINE
RECKFELL ACCOUNT

Вход в аккаунт

Войдите в аккаунт Reckfell.

Один аккаунт для всей платформы Reckfell.