Перейти к основному содержанию
Физический движок RobustToolbox основан на Box2D. Рекомендуется ознакомиться с документацией, если вы планируете работать над движком. Основные изменения:
  • C#-семантика: Версия RobustToolbox изначально основана на порте Box2D от Farseer.
  • Многопоточность: Решатель контактных ограничений и генерация многообразий выполняются внутренне параллельно.
  • Broadphase: В значительной степени кастомный, под нужды SS14, хотя всё ещё использует структуру b2DynamicTree.
  • PhysicsComponent: Был доработан для лучшей интеграции с остальным RobustToolbox, например, с eventbus и сетью.
Сущность с физикой состоит из PhysicsComponent и FixturesComponent. JointsComponent также может присутствовать, но только когда у сущности есть физические соединения. FixturesComponent хранит фикстуры (по сути, подтела внутри сущности), JointsComponent хранит соединения, а PhysicsComponent хранит всё остальное.

Как работает физический конвейер

  1. Запускаются все физические контроллеры. Они применяют внешние силы к физической симуляции, такие как ввод игрока для движения, трение о тайлы сверху и т.д.
  2. Затем шагает каждая физическая карта. Первая часть — поиск всех broadphase-контактов для всех сущностей, которые переместились или иным образом актуальны (через TouchProxies). Мы проверяем некоторые базовые детали, чтобы определить, могут ли 2 тела сталкиваться, например, их слои/маски коллизии, через PreventCollideEvent, соединения, отключающие это, и т.д.
  3. Теперь мы проверяем все наши контакты и определяем, какие из них больше не перекрываются в broadphase, и отбрасываем их.
  4. Запускается Narrowphase. Для всех контактов, которые всё ещё актуальны, мы определяем, действительно ли они перекрываются (это дорого). Выполняется через ManifoldManager. Если перекрытие начинается или заканчивается, создаётся событие StartCollideEvent / EndCollideEvent.
  5. Для всех активных (awake) тел на определённой карте мы создаём так называемые острова. Проще всего представить это так: мы берём активное тело и выполняем поиск пути через все его контакты и соединения насколько это возможно. Затем повторяем этот процесс для следующего активного тела, пока не получим все необходимые острова.
  6. Каждый остров теперь имеет решённые тела.
  7. После завершения решения мы применяем данные обратно к TransformComponent каждого тела и одновременно отправляем MoveEvents.

Команды

physics <overlay> предоставляет множество полезных наложений для отладки физики. showchunkbb показывает все фикстуры сеток.

Непрерывное обнаружение коллизий (CCD)

Физический движок в настоящее время не поддерживает CCD, но может в будущем, когда будет принято решение о том, что поддерживать.

Полезные CVar

angsleeptol Максимальная угловая скорость, разрешённая телу для перехода в спящий режим linsleeptol Максимальная линейная скорость, разрешённая телу для перехода в спящий режим sleepallowed Могут ли физические тела переходить в спящий режим. Не рекомендуется отключать.

PhysicsComponent

Содержит все физические данные для конкретной сущности. Один из способов понять, как работают тела Box2D — это представить их как состоящие из меньших тел — фикстур (например, одна фикстура для хитбокса руки, одна для торса и т.д.), а PhysicsComponent — это общая сущность. Mass: Общая масса тела. Включает массу всех его фикстур. InvMass: Обратная масса. Используется внутри решателя, так как это быстрее, чем Mass. Inertia: Сколько силы требуется для вращения тела. InvI: Обратная инерция. LocalCenter: Центр массы тела. Рассчитывается на основе позиции всех фикстур относительно тела, предполагая отсутствие вращения. physics com отображает это для сеток и тел. FixedRotation: Установка этого значения в true предотвращает изменение angularvelocity тела. LinearDamping: Процент (где 0.2 соответствует 20%), на который LinearVelocity тела уменьшается каждый тик. AngularDamping: Процент (где 0.2 соответствует 20%), на который AngularVelocity тела уменьшается каждый тик. Restitution: Насколько сильно тело “отскакивает” при столкновении. 1.0 соответствует полному отскоку, 0.0 — отсутствию отскока. Это значение смешивается между обоими телами в контакте. Friction: Сколько трения применяется для контакта; также смешивается между двумя телами. Обратите внимание, что это не соответствует трению сверху вниз, которое является искусственным и не задействует контакты.

FixturesComponent

Фикстуры описывают форму и материальные свойства сущностей для целей обнаружения коллизий и других поведений. Они происходят от фикстур Box2D. Фикстуры, не являющиеся hard, не вызывают фактической коллизии, но всё равно генерируют событие коллизии. Это полезно для реализации таких вещей, как скользкость. Каждая фикстура является членом любого количества слоёв коллизии (поле layer). Каждая фикстура также имеет любое количество масок коллизии (поле mask). Сущность A сталкивается с сущностью B, если маски A пересекаются со слоями B. Список слоёв определён в Content.Shared/Physics/CollisionGroup.cs.

CollideOnAnchorComponent

Это компонент, связанный с производительностью. Он включает или выключает (через datafield) коллизию тела при изменении его состояния закрепления.

CollisionWakeComponent

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

VirtualControllers

Для применения непрерывных внешних сил к симуляции, например, движения игрока, следует использовать VirtualControllers. Они запускаются в начале и конце физического шага и позволяют вносить изменения в физические тела. Если вам нужно применить разовые действия, такие как одиночный импульс, можно просто вызвать методы напрямую, например ApplyLinearImpulse.

Contacts (Контакты)

Создаются, когда 2 тела (а именно 2 фикстуры) перекрываются через broadphase. Свойство Enabled устанавливается в true или false по мере перекрытия их narrowphase. Если их broadphase больше не перекрывается, контакт возвращается в пул.

Joints (Соединения)

Применяют ограничения между 2 телами, например, DistanceJoint предотвращает их слишком большое удаление друг от друга. Используйте JointSystem, если хотите их создавать.

Events (События)

Обратите внимание, что приведённые ниже события не вызываются рекурсивно по соображениям производительности. Если ваша сущность неподвижна относительно своего родителя, но родитель движется, MoveEvent не будет создан, например. MoveEvent: Вызывается каждый раз, когда изменяются EntityCoordinates / LocalPosition сущности. Это также относится к изменениям родителя. RotateEvent: Вызывается каждый раз, когда изменяется LocalRotation сущности. EntParentChangedMessage: Вызывается, когда изменяется родитель трансформа сущности. CollisionChangeEvent: Вызывается при включении или выключении CanCollide для тела. Также вызывается при его появлении. PreventCollideEvent: Вызывается, когда 2 тела пытаются перекрыться через broadphase. Следует использовать только для блокировки коллизии 1 конкретной сущности, а не для 2 групп сущностей. Если вы хотите заблокировать 2 целые группы, используйте маски/слои коллизии.

Отладка

Ниже приведён список распространённых проблем и способов их решения: Q. После изменения моё движение стало медленнее обычного. A. В большинстве случаев это означает, что TileFrictionController запускается до MoverController, а не после, и трение тайлов применяется к мобу. Q. Мой моб упирается во что-то твёрдое, а затем телепортируется насквозь. A. Это означает, что клиент считает тело hard-collidable, а сервер — нет, и у вас mispredict. Q. Мой моб отскакивает от чего-то твёрдого. A. Это означает, что сервер считает тело hard-collidable, а клиент — нет, и у вас mispredict. Q. Я могу двигаться только немного, а затем меня возвращает обратно A. Это тоже mispredict, когда клиент думает, что может двигаться, а сервер — нет. Q. Мой ввод задерживается A. Это тоже mispredict, когда сервер думает, что клиент может двигаться, а клиент — нет.

Глоссарий

Constraints (Ограничения): Физическая симуляция применяет силы и подобные воздействия к телам без учёта того, должно ли тело проходить сквозь стену. Ограничения предотвращают это. Box2D не использует этот термин, но контакты и соединения — все являются ограничениями. Некоторые другие физические движки объединяют их под одним термином. Physics Island (Физический остров): Мы решаем физику через острова, а не через все тела последовательно, по причинам, связанным со спящим режимом стопок. Если бы части стопки могли засыпать, пока остальная часть активна, потребовалось бы много времени для стабилизации, если бы вообще удалось. Когда все тела должны засыпать одновременно, это делает систему гораздо более стабильной. Sleeping (Спящий режим): Физические тела перестают обрабатываться (засыпают) через некоторое время по соображениям производительности. Это поведение можно настроить (допуск скорости и время до засыпания) или отключить через cvar.
Последнее изменение 21 июня 2026 г.