Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

Введение в физику

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

Godot предлагает ряд объектов столкновений в 2D и 3D как для обнаружения столкновений, так и для реагирования на них. Определение, какие из них использовать в Вашем проекте, может оказаться сложным. Избежать проблем и упростить разработку можно, если понимать, как работает каждый из них и каковы его достоинства и недостатки.

В этом руководстве вы узнаете:

  • Четыре типа объектов столкновений в Godot

  • Как работает каждый объект столкновений

  • Когда и почему выбирать один тип среди других

Примечание

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

Объекты столкновений

Godot offers four kinds of collision objects which all extend CollisionObject2D. The last three listed below are physics bodies and additionally extend PhysicsBody2D.

  • Area2D

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

  • StaticBody2D

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

  • RigidBody2D

    Это узел реализует симуляцию 2D физики. Вы не управляете RigidBody2D напрямую, а прикладываете к нему силы (гравитация, импульсы и т. д.), и физический движок вычисляет результирующее движение. Подробнее об использовании твёрдых тел.

  • CharacterBody2D

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

Физический материал

Static bodies and rigid bodies can be configured to use a PhysicsMaterial. This allows adjusting the friction and bounce of an object, and set if it's absorbent and/or rough.

Виды и формы коллизий (столкновений)

Физическое тело может содержать любое количество Shape2D объектов в качестве потомков. Эти формы используются для определения границ столкновения объекта и обнаружения контакта с другими объектами.

Примечание

Чтобы обнаруживать столкновения, объекту должен быть назначен хотя бы один Shape2D.

Наиболее распространенным способом назначения формы является добавление CollisionShape2D или CollisionPolygon2D как дочернего элемента объекта. Такие узлы позволят Вам рисовать форму непосредственно в рабочем пространстве редактора.

Важно

Будьте осторожны и никогда не масштабируйте формы столкновений в редакторе. Свойство «Масштаб» в инспекторе должно оставаться (1, 1). При изменении размера формы столкновения всегда следует использовать параметры размера, а не параметры масштаба Node2D. Масштабирование формы может привести к неожиданному поведению при столкновении.

../../_images/player_coll_shape.png

Обратный вызов для физических процессов

The physics engine runs at a fixed rate (a default of 60 iterations per second). This rate is typically different from the frame rate which fluctuates based on what is rendered and available resources.

It is important that all physics related code runs at this fixed rate. Therefore Godot differentiates between physics and idle processing. Code that runs each frame is called idle processing and code that runs on each physics tick is called physics processing. Godot provides two different callbacks, one for each of those processing rates.

The physics callback, Node._physics_process(), is called before each physics step. Any code that needs to access a body's properties should be run in here. This method will be passed a delta parameter, which is a floating-point number equal to the time passed in seconds since the last step. When using the default 60 Hz physics update rate, it will typically be equal to 0.01666... (but not always, see below).

Примечание

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

Слои и маски столкновений

One of the most powerful, but frequently misunderstood, collision features is the collision layer system. This system allows you to build up complex interactions between a variety of objects. The key concepts are layers and masks. Each CollisionObject2D has 32 different physics layers it can interact with.

Рассмотрим каждое из свойств по очереди:

  • collision_layеr

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

  • collision_mаsk

    Здесь описываются слои, которые будут использоваться для сканирования телом столкновений. Если объект не находится в одном из слоев маски, тело игнорирует его. По умолчанию все тела сканируют слой 1.

Эти свойства можно настроить с помощью кода или путем их редактирования в инспекторе.

Отслеживать, для чего используется каждый слой, может быть затруднительно, поэтому может оказаться полезным назначить имена используемым слоям. Имена можно присвоить в меню Настройки проекта - > Имена слоев.

../../_images/physics_layer_names.png

Пример GUI

В игре есть четыре типа узлов: «Стены», «Игрок», «Враг» и «Монета». И Игрок, и Враг должны сталкиваться со стенами. Узел Игрок должен обнаруживать столкновения как с Враг, так и с Монета, но Враг и Монета должны игнорировать друг друга.

Начните с именования слоев 1-4: «walls», «player», «enemies» и «coins» и поместите каждый тип узла в соответствующий слой с помощью свойства «Слой». Затем задайте свойство «Маска» каждого узла, выбрав слои, с которыми он должен взаимодействовать. Например, настройки игрока будут выглядеть следующим образом:

../../_images/player_collision_layers.png ../../_images/player_collision_mask.png

Пример кода

In function calls, layers are specified as a bitmask. Where a function enables all layers by default, the layer mask will be given as 0xffffffff. Your code can use binary, hexadecimal, or decimal notation for layer masks, depending on your preference.

Код, эквивалентный приведенному выше примеру, в котором были включены уровни 1, 3 и 4, будет следующим:

# Example: Setting mask value for enabling layers 1, 3 and 4

# Binary - set the bit corresponding to the layers you want to enable (1, 3, and 4) to 1, set all other bits to 0.
# Note: Layer 32 is the first bit, layer 1 is the last. The mask for layers 4,3 and 1 is therefore
0b00000000_00000000_00000000_00001101
# (This can be shortened to 0b1101)

# Hexadecimal equivalent (1101 binary converted to hexadecimal)
0x000d
# (This value can be shortened to 0xd)

# Decimal - Add the results of 2 to the power of (layer to be enabled - 1).
# (2^(1-1)) + (2^(3-1)) + (2^(4-1)) = 1 + 4 + 8 = 13
pow(2, 1-1) + pow(2, 3-1) + pow(2, 4-1)

Area2D

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

Есть три основных варианта использования Area2D:

  • Переопределение физических параметров (таких как гравитация) в данной области.

  • Обнаружение, когда другие тела входят или выходят из области или какие тела находятся в настоящее время в области.

  • Проверка других областей на перекрытие.

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

StaticBоdy2D

Статическое тело — это тело, которое не перемещается физическим движком. Оно участвует в обнаружении столкновений, но не движется в ответ на столкновение. Однако, оно может передавать движение или вращение столкнувшемуся телу, как если бы оно двигалось, используя его свойства``constant_linear_velocity`` и constant_angular_velocity.

Узлы StaticBody2D чаще всего используются для объектов, которые являются частью среды, или которым не требуется какое-либо динамическое поведение.

Примеры использования StaticBody2D:

  • Платформы (включая движущиеся платформы)

  • Конвейерные ленты

  • Стены и другие препятствия

RigidBоdy2D

Реализует симуляцию 2D-физики. Вы не управляете RigidBody2D напрямую. Вместо этого к нему применяются силы, и физический движок вычисляет результирующее движение, включая столкновения с другими телами и реакции на столкновения, такие как отскок, поворот и т.д.

Поведение твёрдого тела можно изменить с помощью таких свойств, как «Масса», «Трение» или «Отскок», которые можно задать в инспекторе.

На поведение тела также влияют свойства мира, заданные в Свойства проекта - > Физика, или вход в Area2D, переопределяющего глобальные свойства физики.

Когда твёрдое тело находится в состоянии покоя и какое-то время не двигалось, оно «засыпает». Спящее тело ведёт себя как статическое тело, и его силы не рассчитываются физическим движком. Тело будет просыпаться либо при приложении сил, либо при столкновении, либо при помощи кода.

Использование RigidBody2D

Одним из преимуществ использования жесткого тела является то, что различные поведения можно получить «бесплатно», без написания какого-либо кода. Например, если бы Вы делали игру в стиле «Angry Birds» с падающими блоками, Вам нужно было бы просто создать различные RigidBody2D и скорректировать их свойства. Штабелирование, падение и отскок будут автоматически рассчитываться с помощью физического движка.

However, if you do wish to have some control over the body, you should take care - altering the position, linear_velocity, or other physics properties of a rigid body can result in unexpected behavior. If you need to alter any of the physics-related properties, you should use the _integrate_forces() callback instead of _physics_process(). In this callback, you have access to the body's PhysicsDirectBodyState2D, which allows for safely changing properties and synchronizing them with the physics engine.

Например, вот код космического корабля в стиле «Asterioids»:

extends RigidBody2D

var thrust = Vector2(0, -250)
var torque = 20000

func _integrate_forces(state):
    if Input.is_action_pressed("ui_up"):
        state.apply_force(thrust.rotated(rotation))
    else:
        state.apply_force(Vector2())
    var rotation_direction = 0
    if Input.is_action_pressed("ui_right"):
        rotation_direction += 1
    if Input.is_action_pressed("ui_left"):
        rotation_direction -= 1
    state.apply_torque(rotation_direction * torque)

Обратите внимание, что мы не устанавливаем свойства linear_velocity или angular_velocity напрямую, а, скорее, прикладываем силы (thrust и torque) к телу и позволяем физическому движку рассчитать результирующее движение.

Примечание

Когда жесткое тело переходит в спящий режим, функция _integrate_forces() не вызывается. Чтобы переопределить это поведение, необходимо сохранить тело в рабочем состоянии, создав столкновение, применив к нему силу или отключив свойство can_sleep. Помните, что это может негативно сказаться на производительности.

Отслеживание контактов

By default, rigid bodies do not keep track of contacts, because this can require a huge amount of memory if many bodies are in the scene. To enable contact reporting, set the max_contacts_reported property to a non-zero value. The contacts can then be obtained via PhysicsDirectBodyState2D.get_contact_count() and related functions.

Мониторинг контактов через сигналы можно включить с помощью свойства contact_monitor. Список доступных сигналов см. в разделе RigidBody2D.

CharacterBody2D

CharacterBody2D bodies detect collisions with other bodies, but are not affected by physics properties like gravity or friction. Instead, they must be controlled by the user via code. The physics engine will not move a character body.

When moving a character body, you should not set its position directly. Instead, you use the move_and_collide() or move_and_slide() methods. These methods move the body along a given vector, and it will instantly stop if a collision is detected with another body. After the body has collided, any collision response must be coded manually.

Character collision response

After a collision, you may want the body to bounce, to slide along a wall, or to alter the properties of the object it hit. The way you handle collision response depends on which method you used to move the CharacterBody2D.

move_and_collide

При использовании move_and_collide() функция возвращает объект KinematicCollision2D, который содержит информацию о столкновении и столкнувшемся теле. Эту информацию можно использовать для определения реакции.

Например, если нужно найти точку в пространстве, где произошло столкновение:

extends PhysicsBody2D

var velocity = Vector2(250, 250)

func _physics_process(delta):
    var collision_info = move_and_collide(velocity * delta)
    if collision_info:
        var collision_point = collision_info.get_position()

Или отскочить от объекта, с которым столкнулись:

extends PhysicsBody2D

var velocity = Vector2(250, 250)

func _physics_process(delta):
    var collision_info = move_and_collide(velocity * delta)
    if collision_info:
        velocity = velocity.bounce(collision_info.get_normal())

move_and_slide

Скольжение является общей реакцией на столкновение; представьте себе игрока, движущегося вдоль стен в игре с видом сверху, или бегающего вверх и вниз по склонам в платформере. Хотя этот ответ и можно закодировать самостоятельно после использования move_and_collide(), move_and_slide() обеспечивает более удобный способ реализации скользящего движения без написания большого количества кода.

Предупреждение

move_and_slide() автоматически включает временной интервал в свои расчеты, поэтому не умножайте вектор скорости на delta.

Например, используйте следующий код для создания персонажа, который может ходить по земле (включая уклоны) и прыгать, когда стоит на земле:

extends CharacterBody2D

var run_speed = 350
var jump_speed = -1000
var gravity = 2500

func get_input():
    velocity.x = 0
    var right = Input.is_action_pressed('ui_right')
    var left = Input.is_action_pressed('ui_left')
    var jump = Input.is_action_just_pressed('ui_select')

    if is_on_floor() and jump:
        velocity.y = jump_speed
    if right:
        velocity.x += run_speed
    if left:
        velocity.x -= run_speed

func _physics_process(delta):
    velocity.y += gravity * delta
    get_input()
    move_and_slide()

Дополнительные сведения по использованию move_and_slide() см. в разделе Кинематический персонаж (2D), включая демонстрационный проект с подробным кодом.