IO — Ivan Labs

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

3 мин чтения
UnityГеймдев

Заставить геймплей ощущаться синхронизированным с музыкой первые десять секунд легко. Поддержание синхронизации на протяжении всей песни — вот где наивные подходы разваливаются.

Почему простые таймеры дрейфуют

Использование Update() (или любого покадрового аккумулятора delta-time) для отслеживания «насколько далеко мы в песне» накапливает малые ошибки тайминга буквально каждый кадр. По отдельности невидимые, эти ошибки складываются — к концу 3-минутной песни наивный подход на основе таймера может быть заметно рассинхронизирован с реальным воспроизведением звука.

Время кадра не идеально постоянно (даже при стабильной частоте кадров есть джиттер), и таймер, который просто добавляет Time.deltaTime каждый кадр, наследует всю эту неточность кумулятивно.

Исправление: аудиосистема — источник истины

Вместо того чтобы отслеживать время независимо и надеяться, что оно совпадает со звуком, читайте реальную позицию воспроизведения из самой аудиосистемы каждый кадр:

// Наивно (дрейфует со временем):
private float songTime = 0f;
void Update() { songTime += Time.deltaTime; }
 
// Лучше: читать напрямую из источника звука
void Update() {
    float songTime = audioSource.time;
    // Использовать это авторитетное значение для спавна нот / подсчёта очков
}

AudioSource.time отражает, где реально находится аудиосистема в воспроизведении, а не где отдельный аккумулятор предполагает, что она должна быть — это полностью устраняет дрейф, поскольку геймплей теперь читает те же часы, на которых реально работает звук, а не параллельное приближение к ним.

Идём дальше: тайминг с точностью до сэмпла

Для игр, нуждающихся в более жёсткой точности, чем предоставляет AudioSource.time (который обновляется раз за кадр, а не с точностью до сэмпла), распространённый подход использует AudioSettings.dspTime от Unity — собственные высокоточные часы аудиосистемы — планируемые против точных количеств сэмплов, вместо покадрового опроса вообще. Это важнее для по-настоящему соревновательных ритм-игр, чем для казуальных, где покадровой точности от AudioSource.time обычно достаточно.

Предварительный расчёт тайминга нот относительно песни, а не реального времени

Связанный принцип: чарты нот должны создаваться и храниться как временные метки внутри песни (например, «эта нота происходит на 12.450 секунде трека»), а не как последовательность задержек относительно друг друга. Относительные задержки складывают ошибки округления так же, как это делают наивные покадровые таймеры; абсолютные относительные к песне временные метки, сравниваемые с авторитетной позицией воспроизведения, — нет.

Тот же принцип — читать из реального источника истины вместо отслеживания параллельного приближения — постоянно возникает в разработке игр за пределами звука, и стоит усвоить его как общий паттерн, а не просто трюк, специфичный для аудио.

О более широких соображениях по дизайну ощущения ритм-игры (окна тайминга, калибровка, сложность) см. наше руководство по механикам ритм-игры.

Частые вопросы

Почему использование Update() или простого таймера для синхронизации музыки в итоге дрейфует?

Тайминг на основе Update() накапливает малые неточности кадр за кадром — время кадра не идеально постоянно, и эти малые ошибки накапливаются на протяжении многоминутной песни в слышимый, заметный дрейф к концу.

Что следует использовать вместо этого?

Собственную позицию воспроизведения аудиосистемы (AudioSource.time в Unity, или лучше, сэмплы через подход на основе DSP-time) как единый источник истины для «где мы в песне», вместо отдельно работающего игрового таймера, пытающегося отслеживать то же самое независимо.

Имеет ли это значение для игр, не являющихся ритм-играми?

Да, в любой момент, когда геймплею нужно реагировать на биты музыки — динамические музыкальные системы, синхронизированные с битом визуальные эффекты, или чувствительные к таймингу звуковые сигналы в не-ритм-игре имеют тот же лежащий в основе риск дрейфа, если построены на наивном таймере.

Нужна помощь с этим?

Свяжитесь со мной, и я помогу разобраться.

Связаться со мной

Похожие статьи

Поделиться:X / TwitterLinkedIn