Is it possible to get the force of a collision between two objects in Love2D/Box2D? If so, how?
3 Answers
You have access to the b2ContactImpulse in the PostSolve callback on the b2ContactListener. The normalImpulses field on b2ContactImpulse holds an impulse applied to both bodies in the direction of the contact normal.
An impulse is calculated as a change in velocity times the mass of the object, so the normalImpulses well describes how hard the two fixtures hit each other. Given the same relative velocity between two fixtures, the greater the sum of the masses of the fixtures, the greater the normalImpulses.
An impulse is equal to a force acting on an object over a period of time, so if you need to calculate the actual force, you can divide the impulse by the frame time you passed to the Step function and you should get a reasonable assessment of the actual force acting on the two objects, though since i don't understand the inner workings of Box2D, i don't know how accurate this assessment is. If you're just interested in gauging the force of impact, not in the strict physics sense, then using an impulse is just as effective.
World:setCallbacks( beginContact, endContact, preSolve, postSolve ) may solve your problem.
The postSolve function receive two fixtures and a Contact object (https://love2d.org/wiki/Contact).
A very good article: https://love2d.org/wiki/Tutorial:PhysicsCollisionCallbacks
You can use normalimpulse in postSolve() callback.
Here's the example:
force = "" 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) -- make it bouncy 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 = "" -- we'll use this to put info text on the screen later persisting = 0 -- we'll use this to store the state of repeated callback calls 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 -- cleanup when 'text' gets too long 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) love.graphics.print(force, 100, 400) 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 -- only say when they first start touching text = text.."\n"..a:getUserData().." touching "..b:getUserData() elseif persisting < 20 then -- then just start counting text = text.." "..persisting end persisting = persisting + 1 -- keep track of how many updates they've been touching for end function postSolve(a, b, coll, normalimpulse, tangentimpulse) force = normalimpulse end
rigidbody.velocity.magnitudeis a velocity, not a force. And you can't get the force of a collision from the velocity of one object, you need information about the other object as well. Do you want to know how to get the velocity, or the force? And when do you want to get it? \$\endgroup\$