SKWOLETS / DEV LOG / 002

СОЗДАНИЕ
СИСТЕМЫ СТРЕЛЬБЫ

Сервер-авторитетная стрельба с предикцией, перемоткой состояния и реалистичной симуляцией снарядов

СНАРЯД

Я использую термин «снаряд» как обобщающее понятие для любого поражающего объекта, вылетающего из ствола оружия, будь то пуля из автомата Калашникова или граната из подствольного гранатомёта.

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

Я не подхожу к этому как человек, который уже знает все ответы. Большая часть описанного здесь появилась в процессе создания системы, её поломки, тестирования, измерений и последующей переделки тех частей, которые не работали так, как я ожидал.

Я учусь на своих ошибках, потому что для меня это работает лучше всего. Я много времени трачу на тестирование реальной системы в разных условиях, смотрю, что ломается, и пытаюсь понять почему. Эта система стрельбы стала результатом именно этого процесса.

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

ОДНА СИСТЕМА НА ВСЕ СНАРЯДЫ

Снаряды не являются отдельными объектами.

Система не создаёт отдельный игровой объект для каждой снаряда, летящей по миру.

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

Это значительно уменьшает количество накладных расходов на отдельные снаряды и позволяет обрабатывать большое количество летящих пуль, не превращая каждую из них в независимый игровой объект.

СИМУЛЯЦИЯ ЦЕНТРАЛИЗОВАННАЯ
ДАННЫЕ В РАНТАЙМЕ МАССИВ
ОБЪЕКТЫ СНАРЯДОВ НЕ ТРЕБУЮТСЯ
АКТИВНЫЕ СНАРЯДЫ МАСШТАБИРУЕМЫЕ

При нескольких сотнях активных снарядов проход по нативному массиву обходится крайне дёшево по сравнению с поддержанием сотен независимых игровых объектов.

БАЛЛИСТИЧЕСКАЯ СИМУЛЯЦИЯ

Снаряд не просто перемещается из точки А в точку Б.

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

300 М
↓ ГРАВИТАЦИЯ
← СОПРОТИВЛЕНИЕ
↘ ВЕТЕР

Физика намеренно сделана лёгкой и реализована непосредственно на нативном C++. На каждом шаге симуляции скорость снаряда рассчитывается на основе его текущей скорости, гравитации, сопротивления воздуха и ветра. Затем эта скорость используется для расчёта новой позиции снаряда.

Формула: скорость + гравитация + сопротивление + ветер → новая скорость → новая позиция

ГРАВИТАЦИЯ

На протяжении всего полёта на снаряд действует гравитация.

СОПРОТИВЛЕНИЕ

Сопротивление воздуха постепенно уменьшает скорость снаряда по мере его движения.

ВЕТЕР

Скорость и направление ветра может влиять на траекторию снаряда на протяжении всего полёта.

СКОРОСТЬ

Позиция снаряда постоянно рассчитывается на основе его симулируемой скорости.

ПОДХОД, ОСНОВАННЫЙ НА ДАННЫХ

Параметры боеприпасов не зашиты непосредственно в симуляцию полёта снарядов.

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

Это также позволяет модифицировать систему без изменения её внутренней логики. Новые типы боеприпасов можно добавлять, указывая их параметры в конфигурации .

КОНФИГУРАЦИЯ БОЕПРИПАСОВ JSON
[
    {
        "Type": "Ammo_762x39",
        "Velocity": 71800,
        "BulletWeight": 7.9,
        "BallisticCoefficient": 0.294,
        "Lifetime": 3.0,
        "ProjectileCount": 1
    },
    {
        "Type": "Ammo_762x39Tracer",
        "Parent": "Ammo_762x39",
        "Particle": {
            "ParticleSystem": "Tracer",
            "SpawnDelayDistance": 100
        }
    },
    {
        "Type": "Ammo_762x39Subsonic",
        "Parent": "Ammo_762x39",
        "Velocity": 28500,
        "BulletWeight": 12.5,
        "Lifetime": 4.0
    }
]

КЛИЕНТСКАЯ ПРЕДИКЦИЯ

Если ждать ответа сервера перед отображением снаряда, стрельба будет ощущаться медленной.

Поэтому клиент, который выполняет выстрел, запускает ту же симуляцию снаряда локально сразу после создания выстрела.

КЛИЕНТ Предсказанный выстрел мгновенный отклик
ОДНА СИМУЛЯЦИЯ
СЕРВЕР Авторитетный выстрел контроль урона
РЕПЛИКАЦИЯ
ДРУГИЕ КЛИЕНТЫ Визуальный выстрел реплицированное состояние

Важно, что это не три независимых игровых снаряда. Это три выполнения одной и той же детерминированной симуляции снаряда в разных контекстах.

СЕРВЕРНАЯ ВАЛИДАЦИЯ

Предикция нужна для отзывчивости. Сервер остаётся авторитетным.

Прежде чем авторитетный снаряд сможет нанести урон, сервер проверяет выстрел.

ВРЕМЕННАЯ МЕТКА

Не позволяет обработать один и тот же выстрел более одного раза.

ПОЗИЦИЯ СТВОЛА

Сервер восстанавливает ожидаемую позицию в мировом пространстве.

ИНТЕРВАЛ

Выстрел должен соответствовать настроенному интервалу стрельбы оружия.

УГОЛ

Невозможные отклонения от синхронизированного направления камеры могут быть отклонены.

Изначально я использовал скорострельность в RPM, например 600 RPM для оружия типа AK. Позже я перешёл на хранение интервала между выстрелами непосредственно в секундах.
Поэтому 600 RPM превращаются в интервал 0.1 секунды между выстрелами.
Интервал стрельбы также можно индивидуально задавать для каждого оружия. Систему конфигурации оружия я разберу в отдельной статье.

Для проверки угла мне не пришлось придумывать отдельную систему только для репликации камеры. Система движения Unreal Engine уже передаёт необходимую информацию о направлении взгляда через существующий сетевой процесс репликации движения.

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

[12:41:08] выстрел получен

[12:41:08] временная метка принята

[12:41:08] позиция ствола проверена

[12:41:08] интервал стрельбы проверен

[12:41:08] угол камеры проверен

[12:41:08] авторитетный выстрел принят

ПРОСТРАНСТВЕННАЯ СЕТКА

Снаряду не нужно проверять каждого игрока в мире.

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

СНАРЯД НАЧАЛО → КОНЕЦ сегмент траектории
ЗАПРОС СЕТКИ
ПРОСТРАНСТВЕННАЯ СЕТКА КАНДИДАТЫ только близлежащие хитбоксы
ФИЛЬТРАЦИЯ
КОЛЛИЗИЯ ТРАССИРОВКА фактическое пересечение

Поэтому Пространственная Сетка используется как система грубой фазы поиска кандидатов. Более дорогая часть процесса выполняется только для объектов, которые действительно могут находиться рядом с траекторией снаряда.

Я начал называть эту часть системы «Вибриссы», потому что принцип оказался удивительно похожим. Млекопитающим не нужно проверять всё вокруг себя, чтобы понять, что находится рядом. Её усы помогают определить, что находится достаточно близко, чтобы иметь понимание.

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

ПЕРЕМОТКА

Серверу нужно знать не только, где цель находится сейчас. Ему нужно знать, где цель находилась.

К моменту, когда выстрел достигает сервера, цель уже могла переместиться. Перемотка назад позволяет выполнять проверку попадания на историческом состоянии цели в момент, когда был произведён выстрел.

РЕГИСТРАЦИЯ ПОПАДАНИЯ НА СЕРВЕРЕ ПОЗА ЦЕЛИ НА МОМЕНТ ВЫСТРЕЛА −150 мс СНИМОК СЕЙЧАС
ТЕКУЩАЯ
ВЫСТРЕЛ
ИСТОРИЧЕСКАЯ
ПОПАДАНИЕ
ПРОШЛОЕ ВЫСТРЕЛ СЕЙЧАС

Сервер хранит небольшую историю последних позиций целей. Когда выполняется проверка выстрела, выбирается соответствующее историческое состояние и проверка попадания выполняется уже относительно него.
В тестируемой конфигурации полный цикл перемотки и восстановления занимал в среднем около 0.65 микросекунды на операцию, а P95 составлял около 0.9 микросекунды.

PRE-ALPHA

Все показанные материалы находятся на ранней стадии разработки. Визуальные элементы, интерфейс, механики и игровые системы могут измениться в процессе.

Демонстрация ниже записана на дистанции около 160 м при 250 мс сетевой задержки.

РЕГИСТРАЦИЯ ПОПАДАНИЙ

Есть золотое правило, известное в сетевых играх: клиенту нельзя доверять.

Клиентская симуляция нужна для отзывчивости управления и визуальной обратной связи. Но окончательное решение о нанесении урона всегда остается за сервером.

Только подтвержденное сервером попадание может запустить обработку урона.

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

ЗАКЛЮЧЕНИЕ

Эта система не появилась одномоментно. Всё началось с гораздо более простого подхода, в котором сервер доверял клиенту больше, чем следовало. Позже я перешёл к полностью серверному подходу и постепенно построил вокруг него недостающие части.


Около года я собирал эту головоломку, пока все эти детали не сложились в единую модель.