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
![Smile :)](./images/smilies/ms-smile.png)
)
(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
-- Sum of the radii
local sumOfRadii = (self.radius*scale) + (dv.radius*scale)
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
-- Sum of the radii
local sumOfRadii = (self.radius*scale) + (dv.radius*scale)
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