## [Help] Circle Collision detection and reaction

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
andrew.207
Prole
Posts: 19
Joined: Sat Jun 08, 2013 7:27 am
Location: Melbourne, Australia

### [Help] Circle Collision detection and reaction

I'm still very new to LUA/Love2D, so let me know if there are any glaring code structure issues. I have come from a heavy OO background, which I believe is quite bad for LUA.

My project is creating a simple circle collision detection and rebound system. It detects the collision fine, and I'm pretty sure my maths is right, I think my logic is very wrong though. My aim is to have the rebound occur close to real life -- taking into account changes in angle as well as speed.

If anyone could help me out with suggestions or links to examples that'd be a great help. Doesn't have to be LUA examples, can be virtually any language -- even just theory.

BTW I looked through hardon collider and it went way over my head.

The code below is the important bit, within object:update(dt). Each shape has the obvious vars (x, y, speeds, size etc) and also an ID to act as a unique identifier for each object to make this stuff easier.

edit: forgot to say I've already read http://www.wildbunny.co.uk/blog/2011/04 ... r-dummies/ and I just don't understand the last little bit about the vector.

Code: Select all

-- Check if any shapes are colliding with each other
-- Objects are on top of each other if the distance between them is greater than the sum of their radii.
-- For every shape in the shape table
for di,dv in ipairs(shapes) do
if (self.id ~= dv.id) then -- If we're not colliding with ourself
-- Distance between the two circles
H = math.distanceBetween(self.x, self.y, dv.x, dv.y)
local A = self.x - dv.x -- Distance of adjacent edge
-- Angle given when a right angle triangle is created with self and dv
local angleOfCollision = math.deg(1/(math.cos(A/H)))
local levelOfPenetration = H - sumOfRadii -- Level of penetration (-ve if collision)
local forceOfHit = (math.max(self.xSpeed, self.ySpeed) - math.max(dv.xSpeed, dv.ySpeed))

if levelOfPenetration < 0 and forceOfHit > 0 then
-- super basic ejection
if (self.x > dv.x) then
self.x = self.x - levelOfPenetration
else
self.x = self.x + levelOfPenetration
end
if (self.y > dv.y) then
self.y = self.y - levelOfPenetration
else
self.y = self.y + levelOfPenetration
end

-- Apply base speed modifications
local temp = self.xSpeed
if (dv.xSpeed > 0) then
self.xSpeed = self.xSpeed - dv.xSpeed*bounciness
else
self.xSpeed = self.xSpeed + dv.xSpeed*bounciness
end
if (temp > 0) then
dv.xSpeed = dv.xSpeed + temp*bounciness
else
dv.xSpeed = dv.xSpeed - temp*bounciness
end
temp = self.ySpeed
if (dv.ySpeed > 0) then
self.ySpeed = self.ySpeed - dv.ySpeed*bounciness
else
self.ySpeed = self.ySpeed + dv.ySpeed*bounciness
end
if (temp > 0) then
dv.ySpeed = dv.ySpeed + temp*bounciness
else
dv.ySpeed = dv.ySpeed - temp*bounciness
end
--TODO
-- Angle of rebound
-- Ejection
end
end
end

Attachments collide.love
http://www.atunnecliffe.com/
Bitcoins? 1LHn9yaErCakYD2iGwCyLungiC7ACxRUW8

micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

### Re: [Help] Circle Collision detection and reaction

I haven't read your code, but I believe, this blog article might be, what you are looking for.

andrew.207
Prole
Posts: 19
Joined: Sat Jun 08, 2013 7:27 am
Location: Melbourne, Australia

### Re: [Help] Circle Collision detection and reaction

The article was well written and looked nice, but it simply doesn't work properly. When two circles collide, their combined velocity can be higher than it was before the collision, which isn't possible. I'll keep fiddling around.
http://www.atunnecliffe.com/
Bitcoins? 1LHn9yaErCakYD2iGwCyLungiC7ACxRUW8

Banoticus
Citizen
Posts: 60
Joined: Wed Apr 04, 2012 4:01 pm
Location: London

### Re: [Help] Circle Collision detection and reaction

I ran it and all was working well, balls were colliding fine until something happened and they became warp drives!

KaoS
Prole
Posts: 11
Joined: Sat Mar 02, 2013 2:06 am

### Re: [Help] Circle Collision detection and reaction

Hi there. I have been working on something very similar and have gotten it working. There are 2 approaches that you can use. The vector or the angle approach.

before we get started on that I noticed a few minor things what I would change in your code:

(1) on line 8 you say self.x-dv.x
I believe you should invert that to dv.x-self.x for the correct value
(2) line 14 you math max the x and y velocities of both bodies, if the x velocity is greatest in the first object and the y velocity is greatest in the second object you are subtracting unrelated velocities
(3) you are using math.cos to get the angle when math.cos uses the angle to get the fraction of x/dist. math.acos is the correct one (at least that's what I remember. Sorry if I am wrong here )
(4) using math.acos(x/dist) or math.asin(y/dist) or even math.atan(y/x) will give you a correct angle but that angle may be positive when it should be negative, the only way to get an accurate angle is math.atan2(y,x)

anyways looking at your code as it is makes me think you prefer the vector method. At the moment you are using whichever velocity is highest so what we need to do is use both, rather than reflecting the total velocity and breaking it down into x and y again we are going to convert x and y into towards and adjacent, bounce the towards velocity and then convert them back to x and y

Code: Select all

-- Check if any shapes are colliding with each other
-- Objects are on top of each other if the distance between them is greater than the sum of their radii.
-- For every shape in the shape table
for di,dv in ipairs(shapes) do
if (self.id ~= dv.id) then -- If we're not colliding with ourself
-- Distance between the two circles
H = math.distanceBetween(self.x, self.y, dv.x, dv.y)
local totalRelativeVelocity = math.distanceBetween(self.xSpeed, self.ySpeed, dv.xSpeed, dv.ySpeed)
local A = dv.x - self.x -- Distance of adjacent edge
local O = dv.y - self.y -- Distance of opposite edge
local relativeVelocityX = self.xSpeed - dv.xSpeed -- the adjacent/x speed of self relative to dv
local relativeVelocityY = self.ySpeed - dv.ySpeed -- the opposite/y speed of self relative to dv
local collectiveVelocityX = (self.xSpeed + dv.xSpeed) / 2 -- the x speed at which the midpoint of both bodies is travelling
local collectiveVelocityY = (self.ySpeed + dv.ySpeed) / 2 -- the y speed of the center
local angleofVelocity = math.atan2(relativeVelocityY, relativeVelocityX) -- the angle that self is travelling in relative to dv
local angleOfCollision = math.atan2(O, A) -- Angle given when a right angle triangle is created with self and dv
local towardsVel = math.cos(angleOfCollision - angleofVelocity) * totalRelativeVelocity -- this is basically your force of hit thing
local rotaryVel = math.sin(angleOfCollision - angleofVelocity) * totalRelativeVelocity -- this is how fast you are rotating around dv in a clockwise direction
local levelOfPenetration = H - sumOfRadii -- Level of penetration (-ve if collision)

if levelOfPenetration <= 0 and towardsVel > 0 then -- also collide if levelOfPenetration is 0 because they are still touching
--[[basic ejection cancelled as it causes inaccuracies. The bodies will naturally move apart again
if (self.x > dv.x) then
self.x = self.x - levelOfPenetration
else
self.x = self.x + levelOfPenetration
end
if (self.y > dv.y) then
self.y = self.y - levelOfPenetration
else
self.y = self.y + levelOfPenetration
end]]

-- Apply base speed modifications
towardsVel = towardsVel * bounciness

-- Convert velocities back into x and y and add the velocity of the center back into it. We add math.pi to angleOfCollision because it is in the opposite direction
self.xSpeed = ( math.cos( angleOfCollision + math.pi ) * towardsVel + math.cos( angleOfCollision - math.pi / 2 ) * rotaryVel ) / 2 + collectiveVelocityX
self.ySpeed = ( math.sin( angleOfCollision + math.pi ) * towardsVel + math.sin( angleOfCollision - math.pi / 2 ) * rotaryVel ) / 2 + collectiveVelocityY
end
end
end
ALSO please note that you should not change self.xSpeed and self.ySpeed at this point.

if object A and object B are colliding you will calculate object A's new velocity and adjust it accordingly, then you will try to calculate object B's new velocity but you will not get a collision because now A is travelling in the opposite direction. If you by some miracle do get a collision then it will not be correctly calculated because A's velocity has changed

what you should do is

Code: Select all

-- Check if any shapes are colliding with each other
-- Objects are on top of each other if the distance between them is greater than the sum of their radii.
-- For every shape in the shape table
for di,dv in ipairs(shapes) do
if (self.id ~= dv.id) then -- If we're not colliding with ourself
-- Distance between the two circles
H = math.distanceBetween(self.x, self.y, dv.x, dv.y)
local totalRelativeVelocity = math.distanceBetween(self.xSpeed, self.ySpeed, dv.xSpeed, dv.ySpeed)
local A = dv.x - self.x -- Distance of adjacent edge
local O = dv.y - self.y -- Distance of opposite edge
local relativeVelocityX = self.xSpeed - dv.xSpeed -- the adjacent/x speed of self relative to dv
local relativeVelocityY = self.ySpeed - dv.ySpeed -- the opposite/y speed of self relative to dv
local collectiveVelocityX = (self.xSpeed + dv.xSpeed) / 2 -- the x speed at which the midpoint of both bodies is travelling
local collectiveVelocityY = (self.ySpeed + dv.ySpeed) / 2 -- the y speed of the center
local angleofVelocity = math.atan2(relativeVelocityY, relativeVelocityX) -- the angle that self is travelling in relative to dv
local angleOfCollision = math.atan2(O, A) -- Angle given when a right angle triangle is created with self and dv
local towardsVel = math.cos(angleOfCollision - angleofVelocity) * totalRelativeVelocity -- this is basically your force of hit thing
local rotaryVel = math.sin(angleOfCollision - angleofVelocity) * totalRelativeVelocity -- this is how fast you are rotating around dv in a clockwise direction
local levelOfPenetration = H - sumOfRadii -- Level of penetration (-ve if collision)

if levelOfPenetration <= 0 and towardsVel > 0 then -- also collide if levelOfPenetration is 0 because they are still touching
--[[basic ejection cancelled as it causes inaccuracies. The bodies will naturally move apart again
if (self.x > dv.x) then
self.x = self.x - levelOfPenetration
else
self.x = self.x + levelOfPenetration
end
if (self.y > dv.y) then
self.y = self.y - levelOfPenetration
else
self.y = self.y + levelOfPenetration
end]]

-- Apply base speed modifications
towardsVel = towardsVel * bounciness

-- Convert velocities back into x and y and add the velocity of the center back into it. We add math.pi to angleOfCollision because it is in the opposite direction
self.newXSpeed = ( math.cos( angleOfCollision + math.pi ) * towardsVel + math.cos( angleOfCollision - math.pi / 2 ) * rotaryVel ) / 2 + collectiveVelocityX
self.newYSpeed = ( math.sin( angleOfCollision + math.pi ) * towardsVel + math.sin( angleOfCollision - math.pi / 2 ) * rotaryVel ) / 2 + collectiveVelocityY
end
end
end
for di,dv in ipairs(shapes) do
dv.xSpeed = dv.newXSpeed or dv.xSpeed
dv.ySpeed = dv.newYSpeed or dv.ySpeed
end

### Who is online

Users browsing this forum: No registered users and 17 guests