Manuals
Manuals




This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this manual in English

Обработка физических событий в Defold

Ранее физические взаимодействия в Defold обрабатывались через рассылку сообщений всем компонентам сталкивающихся объектов. Однако начиная с версии 1.6.4 Defold предлагает более централизованный подход через функцию physics.set_event_listener(). Эта функция позволяет задать собственный обработчик, который будет принимать все события физических взаимодействий в одном месте, что упрощает код и повышает эффективность.

Установка обработчика физического мира

В Defold каждый collection proxy создает собственный отдельный физический мир. Поэтому при работе с несколькими collection proxy важно учитывать отдельные физические миры, связанные с каждым из них. Чтобы физические события корректно обрабатывались в каждом мире, необходимо устанавливать отдельный обработчик физического мира для мира каждого collection proxy.

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

Ниже приведен пример того, как установить обработчик физического мира внутри collection proxy:

function init(self)
    -- Предполагается, что этот скрипт прикреплен к игровому объекту внутри коллекции,
    -- загруженной этим proxy
    -- Установить обработчик физического мира для физического мира этого collection proxy
    physics.set_event_listener(physics_world_listener)
end

Используя этот подход, вы гарантируете, что каждый физический мир, созданный collection proxy, имеет собственный обработчик. Это особенно важно для эффективной обработки физических событий в проектах, где используется несколько collection proxy.

Если обработчик установлен, physics messages больше не будут отправляться для того физического мира, где установлен этот обработчик.

Структура данных события

Каждое физическое событие передает таблицу data, содержащую специфичную для этого события информацию.

  1. Событие точки контакта (contact_point_event): Это событие сообщает о точке контакта между двумя объектами столкновения. Оно полезно для детальной обработки столкновений, например для расчета силы удара или реализации собственного отклика на столкновение.

    • applied_impulse: Импульс, возникший в результате контакта.
    • distance: Глубина проникновения между объектами.
    • a и b: Объекты, представляющие сталкивающиеся сущности; каждый содержит:
      • position: Мировая позиция точки контакта (vector3).
      • instance_position: Мировая позиция экземпляра игрового объекта (vector3).
      • id: ID экземпляра (hash).
      • group: Группа столкновений (hash).
      • relative_velocity: Скорость относительно другого объекта (vector3).
      • mass: Масса в килограммах (number).
      • normal: Нормаль контакта, направленная от другого объекта (vector3).
  2. Событие столкновения (collision_event): Это событие означает, что между двумя объектами произошло столкновение. Это более обобщенное событие по сравнению с событием точки контакта; оно подходит, когда нужно просто обнаружить факт столкновения без детальной информации о точках контакта.

    • a и b: Объекты, представляющие сталкивающиеся сущности; каждый содержит:
      • position: Мировая позиция (vector3).
      • id: ID экземпляра (hash).
      • group: Группа столкновений (hash).
  3. Событие триггера (trigger_event): Это событие отправляется, когда объект взаимодействует с trigger-объектом. Оно полезно для создания областей в игре, которые вызывают какое-либо действие при входе объекта или выходе из них.

    • enter: Указывает, было ли взаимодействие входом (true) или выходом (false).
    • a и b: Объекты, участвующие в событии триггера; каждый содержит:
      • id: ID экземпляра (hash).
      • group: Группа столкновений (hash).
  4. Ответ ray cast (ray_cast_response): Это событие отправляется в ответ на raycast и содержит информацию об объекте, который был пересечен лучом.

    • group: Группа столкновений объекта, в который попал луч (hash).
    • request_id: Идентификатор запроса raycast (number).
    • position: Позиция попадания (vector3).
    • fraction: Доля длины луча, на которой произошло попадание (number).
    • normal: Нормаль в точке попадания (vector3).
    • id: ID экземпляра объекта, в который попал луч (hash).
  5. Промах ray cast (ray_cast_missed): Это событие отправляется, если raycast не попал ни в один объект.

    • request_id: Идентификатор запроса raycast, который не дал попадания (number).

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

local function physics_world_listener(self, events)
    for _,event in ipairs(events) do
        if event.type == hash("contact_point_event") then
            -- Обработка детализированных данных точки контакта
            pprint(event)
        elseif event.type == hash("collision_event") then
            -- Обработка общих данных о столкновении
            pprint(event)
        elseif event.type == hash("trigger_event") then
            -- Обработка данных взаимодействия с триггером
            pprint(event)
        elseif event.type == hash("ray_cast_response") then
            -- Обработка данных о попадании raycast
            pprint(event)
        elseif event.type == hash("ray_cast_missed") then
            -- Обработка случая, когда raycast не попал
            pprint(event)
        end
    end
end

function init(self)
    physics.set_event_listener(physics_world_listener)
end

Ограничения

Обработчик вызывается синхронно в момент возникновения события. Это происходит в середине временного шага, а значит физический мир в этот момент заблокирован. Из-за этого нельзя использовать функции, которые могут повлиять на симуляцию физического мира, например physics.create_joint().

Ниже приведен небольшой пример того, как можно обойти это ограничение:

local function physics_world_listener(self, events)
    for _,event in ipairs(events) do
        if event.type == hash("contact_point_event") then
            local position_a = event.a.normal * SIZE
            local position_b =  event.b.normal * SIZE
            local url_a = msg.url(nil, event.a.id, "collisionobject")
            local url_b = msg.url(nil, event.b.id, "collisionobject")
            -- сформировать сообщение так же, как должны передаваться аргументы в `physics.create_joint()`
            local message = {physics.JOINT_TYPE_FIXED, url_a, "joind_id", position_a, url_b, position_b, {max_length = SIZE}}
            -- отправить сообщение самому объекту
            msg.post(".", "create_joint", message)
        end
    end
end

function on_message(self, message_id, message)
    if message_id == hash("create_joint") then
        -- распаковать сообщение в аргументы функции
        physics.create_joint(unpack(message))
    end
end

function init(self)
    physics.set_event_listener(physics_world_listener)
end