Участник:Dngcher/Песочница
Система взрывов
Система взрывов это стандартная механика, отвечающая за расчёт урона, разрушение плиток, физические эффекты (броски объектов), воспламенение, звук и визуализацию взрывов. Всё управляется через прототипы взрывов (ExplosionPrototype), компоненты-триггеры (ExplosiveComponent, ScatteringGrenadeComponent, ProjectileGrenadeComponent) и системы (ExplosionSystem).
Прототип взрыва (ExplosionPrototype)
Это YAML-описание типа взрыва. Все поля определяют, как взрыв наносит урон, разрушает плитки, выглядит и звучит.
| Поле | Тип | Описание |
|---|---|---|
damagePerIntensity |
DamageSpecifier |
Базовый урон за единицу интенсивности. На каждом тайле итоговый урон это damagePerIntensity * интенсивность_на_плитке. Виды урона: Heat, Blunt, Piercing, Structural, Cold и другие.
|
fireStacks |
float? |
Количество Stacks огня, добавляемых загоревшимся сущностям (если есть FlammableComponent).
|
temperature |
float? |
Температура, до которой нагревается атмосфера в затронутых тайлах. |
tileBreakChance и tileBreakIntensity |
float[] |
Два массива одинаковой длины, задающие функцию зависимости шанса разрушения плитки от интенсивности. Шанс интерполируется линейно между точками (intensity -> chance).
|
tileBreakRerollReduction |
float |
После успешного разрушения плитки интенсивность уменьшается на это значение, и делается повторная попытка разрушить следующий слой (например, чтобы пробить покрытие). Это позволяет сильным взрывам создавать разгерметизации. |
lightColor |
Color |
Цвет точечного света в эпицентре взрыва. |
fireColor |
Color? |
Цвет, которым модулируется текстура огня на плитках. Если не задан то, используется стандартный. |
texturePath |
ResPath |
Путь к RSI-файлу с текстурами огня. |
fireStates |
int |
Количество состояний (градаций) текстуры огня. |
intensityPerState |
float |
Порог интенсивности для перехода к следующему состоянию огня. |
smallSoundIterationThreshold |
int |
Если количество итераций (колец) взрыва меньше этого числа то, взрыв считается маленьким и проигрывается тихий звук. |
sound, smallSound, soundFar, smallSoundFar |
SoundSpecifier |
Звуки для обычного и маленького взрыва (ближний и дальний). |
maxCombineDistance |
float |
Если два взрыва одного прототипа происходят в одном тике и эпицентры ближе этого расстояния то, их интенсивность суммируется (они объединяются в один взрыв). |
Интенсивность и её распределение
Главное значение это интенсивность взрыва. Она определяется в момент вызова взрыва (через QueueExplosion или TriggerExplosive).
Основные параметры распространения
totalIntensity— суммарная энергия взрыва. Она распределяется по плиткам.slope— скорость падения интенсивности с расстоянием (на каждую плитку). Чем большеslope, тем быстрее затухает взрыв.maxTileIntensity— максимальная интенсивность, которая может быть на одной плитке. Это ограничивает локальный урон и шанс разрушения.
Радиус взрыва определяется косвенно: общая интенсивность примерно равна объёму конуса взрыва (интенсивность как высота). Чем больше totalIntensity, тем больше площадь.
Алгоритм заливки
- Начинается с заданных координат эпицентра взрыва (плитка или точка в космосе).
- На каждой итерации добавляет соседние плитки (сначала ортогональные, затем диагональные).
- Интенсивность каждой новой итерации определяется по формуле
stepSize = slope / 2(или меньше, если осталось мало интенсивности). - Процесс продолжается, пока не кончится
totalIntensityили не будет достигнут лимит итераций/площади.
Таким образом, плитки на разных расстояниях получают разные значения интенсивности.
Расчёт урона и сопротивление
Урон по сущностям вычисляется для каждой затронутой плитки отдельно.
- Базовый урон это
damagePerIntensity * интенсивность_на_плитке. - Учитываются глобальные модификаторы урона от взрывов (через
DamageableSystem). - Для каждой сущности на плитке вызывается событие
GetExplosionResistanceEvent(передаётся ID прототипа). Это позволяет модифицировать коэффициент урона (например, броня, скафандры). - Если сущность имеет
ExplosionResistanceComponent, коэффициент защиты применяется перед нанесением урона. - После всех модификаций урон наносится через
DamageableSystem.TryChangeDamage(игнорируя обычные резисты, т.к. резисты ко взрывам уже учтены).
Вложенные контейнеры и экипировка
Событие BeforeExplodeEvent позволяет рекурсивно пробрасывать урон на содержимое контейнеров (рюкзаки, карманы). Это позволяет, например, взрывать предметы внутри сумки игрока, если сам игрок получил урон. Коэффициент урона для содержимого также может быть изменён.
Разрушение плиток
Взрыв может разрушать плитки (стены, полы). Этот процесс идёт параллельно с нанесением урона сущностям.
- Шанс разрушения определяется функцией
TileBreakChance(intensity)- линейная интерполяция между точками изtileBreakChanceиtileBreakIntensity. - Если плитка разрушена то, она меняется на покрытие (или на следующий слой из истории плиток, если есть
TileHistoryComponent). - После каждого успешного разрушения интенсивность уменьшается на
tileBreakRerollReduction, и шанс пересчитывается для следующего слоя. Это позволяет сильным взрывам пробивать несколько слоёв стен. - Если новая плитка помечена как
MapAtmosphere = true, это означает создание вакуума - но только если взрыву разрешено создавать вакуум (canCreateVacuum).
Ограничения: maxTileBreak - сколько раз может разрушиться один тайл (обычно бесконечно), tileBreakScale это множитель интенсивности для расчёта разрушения (можно сделать взрыв сильнее по урону, но слабее по пробитию).
Визуализация и звук
Визуал отрисовывается на клиенте через ExplosionOverlay. Сервер создаёт специальную сущность с компонентом ExplosionVisualsComponent, которая передаёт клиенту:
- координаты эпицентра,
- наборы плиток по итерациям,
- список интенсивностей,
- матрицу трансформации для космоса.
Клиент подгружает текстуры из прототипа и рисует их с соответствующим цветом и прозрачностью, в зависимости от текущей интенсивности и прогресса взрыва (номер итерации).
Звук воспроизводится сразу в момент создания взрыва (сервер отправляет звуковые события игрокам в радиусе). Есть два набора звуков — для близкого и дальнего расстояния.
Компоненты-триггеры взрывов
ExplosiveComponent
Самый простой - содержит ссылку на прототип и параметры:
ExplosionType- ID прототипа.TotalIntensity,IntensitySlope,MaxIntensity- могут переопределять значения по умолчанию.TileBreakScale,MaxTileBreak,CanCreateVacuum- управляют разрушением тайлов.Repeatable- может ли взрываться несколько раз.DeleteAfterExplosion— удалять ли сущность после взрыва.
Система вызывает TriggerExplosive, который ставит взрыв в очередь.
ScatteringGrenadeComponent
Используется для гранат, которые разлетаются на множество мелких взрывающихся объектов. При срабатывании (триггер по ключу) содержимое контейнера (и неразмноженные экземпляры) разбрасываются в стороны с заданной скоростью и случайными углами. Затем у каждого выброшенного объекта может быть активирован таймер (Trigger) через заданные задержки.
Параметры:
FillPrototype- прототип для заполнения, если контейнер пуст.Capacity- максимальное количество вылетающих entity (можно добавить любую entity, которая будет вылетать при активации этой гранаты).Velocity,Distance,RandomAngle,RandomDistance- параметры разлёта.DelayBeforeTriggerContents,IntervalBetweenTriggers— задержки активации.
ProjectileGrenadeComponent
Похож на ScatteringGrenade, но выбрасывает не взрывающиеся объекты, а снаряды (projectiles), которые летят с заданной скоростью и наносят урон при попадании (через систему стрельбы).
Сопротивление взрывам (ExplosionResistanceComponent)
Этот компонент можно добавить предмету (например, сумке) или на саму сущность. Он уменьшает или увеличивает получаемый урон от взрывов в зависимости от коэффициента.
DamageCoefficient- множитель (2 - урон в два раза больше, 1 - нет защиты, 0 - полная неуязвимость).Modifiers- словарь, где ключ - ID прототипа взрыва, значение - дополнительный множитель именно для этого типа. Это позволяет иметь разную защиту от разных взрывов.Worn- еслиtrue, то действует только когда предмет надет (черезInventoryRelayedEvent).Examine- строка локализации для отображения в осмотре.
Пример из explosion.yml
- type: explosion
id: Default
damagePerIntensity:
types:
Heat: 5
Blunt: 5
Piercing: 5
tileBreakChance: [0, 0.5, 1]
tileBreakIntensity: [0, 10, 30]
tileBreakRerollReduction: 20
lightColor: Orange
texturePath: /Textures/Effects/fire.rsi
fireStates: 3
temperature: 800
Default- ID прототипа.damagePerIntensity- количество урона за одну единицу интенсивности на всём тайле.tileBreakChanceиtileBreakIntensity- шанс разрушения плитки: при интенсивности 0 -> 0%, при 10 -> 50%, при 30 -> 100%.tileBreakRerollReduction- после разрушения интенсивность уменьшается на 20, и проверяется следующий слой.lightColor- цвет вспышки в эпицентре взрыва.texturePath- путь к текстуре взрыва.fireStates- то, какойState(написанный вmeta.json) используется при взрывеtemperature- температура атмосферы после взрыва
Очередь и объединение взрывов
Взрывы ставятся в очередь (_explosionQueue) и обрабатываются понемногу за тик, чтобы не нагружать сервер. Если два взрыва одного прототипа происходят в одном тике и их эпицентры ближе maxCombineDistance, их интенсивности суммируются. Это позволяет, например, детонировать одновременно несколько бомб, создавая один мощный взрыв вместо нескольких маленьких.