Tutorial:PhysicsCollisionCallbacks (Русский)

Предисловие

Если у вас слабое представление о love.physics, вам нужно сначала ознакомиться с вводным руководством по использованию физики.

В этом руководстве мы создадим столкновение между двумя объектами, которое вызывается определенной callback-функцией (колбеком), заданной в World:setCallbacks.


Руководство

Создание main.lua

Начнем с создания main.lua с тремя функциями: love.load, love.update, and love.draw

function love.load()
	
end
function love.update(dt)
	
end
function love.draw()
	
end

Настройка мира

Сейчас, когда фреймворк уже настроен, настроим физический мир в love.load с newWorld. Мы также будем использовать World:update.

function love.load()
	-- Помещаем новый "мир" в переменную "world"
	world = love.physics.newWorld(0, 200, true)  -- Гравитация установлена равной 0 по оси X и 200 по оси Y
end

function love.update(dt)
	-- Никогда не забывайте обновлять мир каждый кадр
	world:update(dt)
end

Теперь нам нужно создать мяс и землю. Чтобы это сделать нам потребуются newBody, newCircleShape и newRectangleShape. Тела к формам мы будем прикреплять с поощью newFixture.

function love.load()
    ... -- не пишите это, это просто обозначение, в какой функции, в каком месте должен быть этот код

    ball = {}
	ball.b = love.physics.newBody(world, 400,200, "dynamic")  -- устанавливаем x,y координаты (400,200) и позволяем двигаться и задевать другие объекты ("dynamic")
	ball.b:setMass(10)                                        -- делаем довольно легким
	ball.s = love.physics.newCircleShape(50)                  -- устанвливаем радиус 50
	ball.f = love.physics.newFixture(ball.b, ball.s)          -- прикрепляем тело к форме
	ball.f:setRestitution(0.4)                                -- делаем упругим
	ball.f:setUserData("Ball")                                -- устанавливаем имя, которое будем использовать для достпуа к объекту позже
	  
	static = {}
	static.b = love.physics.newBody(world, 400,400, "static") -- "static" делает объект неподвижным
	static.s = love.physics.newRectangleShape(200,50)         -- устанавливаем размер 200,50 (x,y)
	static.f = love.physics.newFixture(static.b, static.s)
	static.f:setUserData("Block")
end

Объекты уже созданы, но вы все еще не видите их. Давайте нарисуем их.

function love.draw()
    love.graphics.circle("line", ball.b:getX(),ball.b:getY(), ball.s:getRadius(), 20)
    love.graphics.polygon("line", static.b:getWorldPoints(static.s:getPoints()))
end

Теперь мы можем увидеть мяч, падающий вниз на прямоугольник

World Callbacks

Но что если нам нужно больше информации о сталкновении этих объектов? Теперь мы будем использовать World:setCallbacks для дальнейшего анализа их столкновений.

Первое, что мы сделаем, это установим колбеки для нашего мира с World:setCallbacks. Есть четыре колбека для столкновения: beginContact, endContact, preSolve и postSolve.

beginContact вызывается когда две привязки (fixtures) начинают перекрывать друг друга (два объекта сталкиваются).
endContact вызывается когда две привязки (fixtures) перестаются перекрывать друг друга (два объекта разъединяются).
preSolve вызывается непосредственно перед завершение кадра для текущего столкновения
postSolve вызывается непосредственно после завершения кадра для текущего столкновения
function love.load()
    ...  -- добавить к остальному love.load

    world = love.physics.newWorld(0, 200, true)
        -- Названия этих колбеков могут быть такими, как вы захотите:
        world:setCallbacks(beginContact, endContact, preSolve, postSolve)

    text       = ""   -- мы будем испольовать позже эту переменную для размещения информации на экране
    persisting = 0    -- мы будем использовать это, чтобы сохранить состояние неоднократных вызовов колбека

Теперь объявим эти функции.

function beginContact(a, b, coll)
	
end

function endContact(a, b, coll)
	
end

function preSolve(a, b, coll)
	
end

function postSolve(a, b, coll)
	
end

Эти функции происходят каждрый раз, когда происходит какое-то из действий при столкновении. Они принимают две привязки (fixtures) и объект столкновения (collision object). Эти параметры так же могут быть названы как вам захочется. В этом руководстве мы назовем их a, b и coll.

Допустим, мы хотим напечатать информацию на экран каждый раз, когда колбек вызван. Нам всего лишь нужно изменить переменную text, которую мы добавили в love.load() ранее, добавляя строку каждый раз, когда происходит столкновение. Нам также нужно немного кода, чтобы поддерживать вывод (лог) чистым.

Список функций, которые вы можете использовать с контактами может быть найден на странице Contact.

function love.update(dt)
    ... -- добавить к остальному love.update

    if string.len(text) > 768 then    -- очистка, когда 'text' становится слишком длинным
        text = "" 
    end
end

function love.draw()
    ... -- добавить к остальному love.draw

    love.graphics.print(text, 10, 10)
end

function beginContact(a, b, coll)
    x,y = coll:getNormal()
    text = text.."\n"..a:getUserData().." colliding with "..b:getUserData().." with a vector normal of: "..x..", "..y
end


function endContact(a, b, coll)
    persisting = 0    -- сбрасываем, когда они перестают соприкасаться
    text = text.."\n"..a:getUserData().." uncolliding with "..b:getUserData()
end

function preSolve(a, b, coll)
    if persisting == 0 then    -- только говорит, когда они впервые начинают касаться
        text.."\n"..a:getUserData().." touching "..b:getUserData()
    elseif persisting < 20 then    -- затем просто начинается вести отсчет
        text = text.." "..persisting
    end
    persisting = persisting + 1    -- отслеживает как много обновлений они соприкасались
end

function postSolve(a, b, coll)
-- мы не будем ничего делать с этой функцией
end

Теперь вы знаете как использовать колбеки физического мира (world callbacks)!

Чтобы лучше понять как мир себя ведет и видеть когда колбеки вызываются, добавим элементы управления, позволяющие подкидывать, толкать мяч:

function love.update(dt)
    world:update(dt)

    if love.keyboard.isDown("right") then
        ball.b:applyForce(1000, 0) 
    elseif love.keyboard.isDown("left") then
        ball.b:applyForce(-1000, 0) 
    end
    if love.keyboard.isDown("up") then
        ball.b:applyForce(0, -5000)
    elseif love.keyboard.isDown("down") then
        ball.b:applyForce(0, 1000)
    end

    if string.len(text) > 768 then    -- очищаем когда 'text' станет слишком длинным
        text = "" 
    end
end


Завершение

Скриншоты

Скриншот результата.

main.lua

function love.load()
    world = love.physics.newWorld(0, 200, true)
        world:setCallbacks(beginContact, endContact, preSolve, postSolve)

    ball = {}
        ball.b = love.physics.newBody(world, 400,200, "dynamic")
        ball.b:setMass(10)
        ball.s = love.physics.newCircleShape(50)
        ball.f = love.physics.newFixture(ball.b, ball.s)
        ball.f:setRestitution(0.4)    -- делаем упругим
        ball.f:setUserData("Ball")
    static = {}
        static.b = love.physics.newBody(world, 400,400, "static")
        static.s = love.physics.newRectangleShape(200,50)
        static.f = love.physics.newFixture(static.b, static.s)
        static.f:setUserData("Block")

    text       = ""   -- будем использовать эту переменную для вывода на экран
    persisting = 0    -- будем использовать, чтобы сохранить состояние неоднократных вызовов колбека
end

function love.update(dt)
    world:update(dt)

    if love.keyboard.isDown("right") then
        ball.b:applyForce(1000, 0)
    elseif love.keyboard.isDown("left") then
        ball.b:applyForce(-1000, 0)
    end
    if love.keyboard.isDown("up") then
        ball.b:applyForce(0, -5000)
    elseif love.keyboard.isDown("down") then
        ball.b:applyForce(0, 1000)
    end

    if string.len(text) > 768 then    -- очищаем 'text' становится слишком длинным
        text = ""
    end
end

function love.draw()
    love.graphics.circle("line", ball.b:getX(),ball.b:getY(), ball.s:getRadius(), 20)
    love.graphics.polygon("line", static.b:getWorldPoints(static.s:getPoints()))

    love.graphics.print(text, 10, 10)
end

function beginContact(a, b, coll)
    x,y = coll:getNormal()
    text = text.."\n"..a:getUserData().." colliding with "..b:getUserData().." with a vector normal of: "..x..", "..y
end

function endContact(a, b, coll)
    persisting = 0
    text = text.."\n"..a:getUserData().." uncolliding with "..b:getUserData()
end

function preSolve(a, b, coll)
    if persisting == 0 then    -- только говорит, когда они впервые начинают касаться
        text = text.."\n"..a:getUserData().." touching "..b:getUserData()
    elseif persisting < 20 then    -- затем просто начинается вести отсчет
        text = text.." "..persisting
    end
    persisting = persisting + 1    -- отслеживает как много обновлений они соприкасались
end

function postSolve(a, b, coll)
end



Другие языки