Физические энтити на клиенте и сервере
January 2024
Обзор
Основным преимуществом движка Source является физическая имитация твердых тел. Эта имитация реализует основную механику и физику Ньютона такие как гравитация, траектория, трение, столкновения, прыжки и плавание. Модели поддерживают эту имитация предоставляя информацию о их модели коллизий, типе материалов, весе и т.д. В режиме однопользовательской игры все физические энтити контроллируются и имитируются сервером (физика на стороне сервера) и передаются клиенту. В режиме многопользовательской игры мелкие объекты подобные консервным банкам или бутылкам которые не влияют на геймплей, полностью имитируются на стороне клиента и поэтому не синхронизируется между клиентами. Это необходимо в связи с тем что движение физических энтитей создает значительный сетевой траффик с того момента как они изменяют их позицию и базовое направление в каждом кадре. Сообщение по сети этих изменений может почти полностью блокировать любое соединение как только начнут двигаться множество физизических объектов в одно время (взрывы и т.д.). Физ-ие объекты на стороне клиента не влияют на передвижение игрока и они всегда должны быть гораздо меньше чем сам игрок, так что игрок не может спрятаться за ними. Во время разрушения серверных бьющихся объектов, они разбиваются на меньшие фрагменты имитирующиеся на стороне клиента.
Добавление физических энтитей
Для маппера довольно просто помещать физические энтити в Хаммере. Для однопользовательских карт класс энтити physics_prop должен быть использован для создания контроллируемых серверной стороной энтитей. Игрок выполняет корректную коллизию с этими энтитями, может проходить сквозь них, двигать их с разных сторон или поднимать их. К сожалению этот класс не может использоваться в многопользовательской игре так как взаимодействие игрока с физическими энтитями не предсказывается на стороне клиента и может вызвать неестественное или задержанное движение. Поэтому должен быть использован специальный класс энтити prop_physics_multiplayer, который реализует более упрощенное поведение коллизии (COLLISION_GROUP_PUSHAWAY
). Многопользовательские физ-ие энтити только могут толкаться, но нельзя ходить сквозь них или поднимать их. Если они больше, игрок просто отталкивается. Становятся ли многопользовательские физ-ие энтити серверными или имитируются на стороне клиента, определяется в объекте модели.
Физические свойства модели определяются в её .QC файле с помощью 3 секций: $surfaceprop
, $collisionmodel
, $keyvalues
. Первая секция $surfaceprop
устанавливает свойства поверхности модели как описано в текстовом файле \scripts\surfaceproperties.txt
. Тут описываются свойства типа трения, эластичности и звука коллизий. Следующая секция $collisionmodel
устанавливает модель коллизий и вес объекта. Для простмотра модели коллизий для энтитей в игре включите консольную переменную "vcollide_wireframe 1"
. Третья секция "prop_data"
включенная в $keyvalues
. Здесь описываются свойства объекта например кол-во здоровья, разбиваемость модели и физический режим описанные в (полном описание prop_data ). Ниже приводится пример:
$surfaceprop "cardboard" // свойства поверхности объекта $collisionmodel "mymodel.smd" { $Mass 40 // Масса в килограммах $concave } $keyvalues { "prop_data" { "base" "Cardboard.Medium" // базовый материал определенный в propdata.txt "health" "40" // перезапись свойств материала "physicsmode" "1" // устанока произвольного режима физики } }
Режим физики определяется если объект является серверной или клиентской физической энтитей. Существует 2 серверных режима и один клиентский, описаны они в props_shared.h
:
#define PHYSICS_MULTIPLAYER_AUTODETECT 0 // режим автоопределения основанный на массе и размере #define PHYSICS_MULTIPLAYER_SOLID 1 // серверный, монолитный (сталкивается с игроком) #define PHYSICS_MULTIPLAYER_NON_SOLID 2 // серверный, не монолитный #define PHYSICS_MULTIPLAYER_CLIENTSIDE 3 // клиентский, не монолитный
Если свойство физического режима не установлено в propdata.txt или в QC file, режим физики может быть получен из функции GetAutoMultiplayerPhysicsMode()
основанный на размере и весе модели. Модель становиться клиентской физ-ой энтитей, если её размер меньше определенной границы (устанавливается консольной переменной sv_pushaway_clientside_size).
Группы коллизий
По нескольким причинам нет необходимости чтоб все динамические физ-ие энтити вызывали коллизии между собой. Это может быть по разным причинам геймплея, но чаще это делается по причине производительности. Особенно создание множества мелких, быстро движущихся кусков осколков которые могут значительно снизить производительность. Для визуального эффекта бющихся осколков или взрывающихся подобных частицам объектов достаточно их коллизии с статическими предметами мира, но не самих с собой или других динамических объектов. Для реализации специального поведения коллизий движок Source позволяет определять группы коллизий и указывать если элементы этих групп сталкиваться или нет. Каждая энтитя принадлежит только одной группе одновременно, устанавливается это функцией SetCollisionGroup()
. Новые группы и новые правила могут быть легко добавлены. Подсистемы физики запрашивают виртуальную функцию bool CGameRules::ShouldCollide(int group0, int group1)
чтобы для определения должна ли вызываться проверка коллизии между двумя объектами.
Изначально определены группы:
COLLISION_GROUP_NONE |
По умолчанию, сталкивается с статическими и динамическими объектами |
COLLISION_GROUP_DEBRIS |
Сталкивается только с миром и статическими вещами |
COLLISION_GROUP_DEBRIS_TRIGGER |
Тоже что и предыдущее, но переключает триггеры |
COLLISION_GROUP_INTERACTIVE_DEBRIS |
Сталкивается со всем за исключением других осколков или интерактивных осколков |
COLLISION_GROUP_INTERACTIVE |
Сталкивается со всем за исключением осколков или интерактивных осколков |
COLLISION_GROUP_PLAYER |
Группа коллизий игрока |
COLLISION_GROUP_BREAKABLE_GLASS |
Специальная группа для стеклянных осколков |
COLLISION_GROUP_VEHICLE |
Группа коллизии для управляемого транспорта |
COLLISION_GROUP_PLAYER_MOVEMENT |
Для однопользовательского режима то же чо Collision_Group_Player, для многопользовательского - это отфильтровывает других игроков и CBaseObjects |
COLLISION_GROUP_NPC |
Базовая группа NPC |
COLLISION_GROUP_IN_VEHICLE |
Любая энтить внутри транспорта |
COLLISION_GROUP_WEAPON |
Для любого огружия требующего определения коллизий |
COLLISION_GROUP_VEHICLE_CLIP |
Клип браш для транспорта, для ограничения движения транспорта |
COLLISION_GROUP_PROJECTILE |
Пули |
COLLISION_GROUP_DOOR_BLOCKER |
Блокировка энтитей не допустимых к движению рядом с дверями |
COLLISION_GROUP_PASSABLE_DOOR |
Двери с которыми не может сталкиваться игрок |
COLLISION_GROUP_DISSOLVING |
Вещи проникают друг в друга |
COLLISION_GROUP_PUSHAWAY |
Немонолитные на стороне клиента и сервера, отталкивают игрока |
Дополнение
Список допустимых комманд в секции $collisionmodel
модели:
$mass |
Установка массы модели вручную |
$automass |
Указывает физической системе вычислять массу для модели, основанную на её свойствах поверхности и размерах |
$inertia |
Инерциальная величина |
$damping |
Линейное торможение |
$rotdamping |
Вращательное торможение |
$drag |
Изменяет плотность (сопротивление) воздуха |
$concave |
Физ-ая модель коллизий не одна выпуклая оболочка. Если это не установлено, создается одна сплошная выпуклая оболочка вокруг данной геометрии. |
$masscenter |
Переопределяет центр масс в локальных координатах |
$jointskip |
Редко используется. Исключает джоинт в модели коллизий из использования. (например если используется рендеринг модели тряпичной куклы, и в ней есть кости которые не нужны) |
$jointmerge |
Соединяет прикрепление вертексов для двух джоинтов. |
$rootbone |
Наиболее главная кость обладающая коллизией. |
$jointconstrain |
Ограничения движения джоинтов |
$jointinertia |
Подобно $inertia , но отдельно для кости
|
$jointdamping |
Подобно $damping , но отдельно для кости
|
$jointrotdamping |
Подобно $rotdamping , но отдельно для кости
|
$jointmassbias |
Масса автоматически распространяется в объеме, это позволяет сместить ее к костям |
$noselfcollisions |
Отключить все коллизии между костями в данной модели, обычно для производительности |
$jointcollide |
Если указаны какие-то из $jointcollide , только эти джоинты подвергаются коллизии между собой
|
$animatedfriction |
Используется для анимирования значаения трения на джоинтах во времени. |
Список доступных записей для определений в propdata.txt:
base |
Указывает базовый класс от которого происходит наследование (базовые типы перечисляются в propdata.txt) |
blockLOS |
Переопределяет какая из опор может блокировать линию видимости для NPC |
AIWalkable |
Переопределяет какой из AI примет эту опору как проходимую. |
dmg.bullets |
Повреждения от пуль создаются на этой опоре. |
dmg.club |
Повреждения от ударов создаются на этой опоре. |
dmg.explosive |
Повреждения от взрывов создаются на этой опоре. |
health |
Количество здоровья которое нужно убрать для разрушения. |
explosive_damage |
Эта опора создает взрывное повреждение. |
explosive_radius |
Радиус взрыва создаваемый этой опорой когда она разрушается. |
breakable_model |
Тип разбиваемых осколков на которые может развалиться эта опора |
breakable_count |
Число осколков на которе распадается эта опора. |
allowstatic |
Позволяет этой опоре быть статической или физически имитируемой. |
physicsmode |
Устанавливает поведение сетевой физики (1 - полная, 2 - не монолитная, 3 - на стороне клиента) |