Learn the fundamentals of the popular programming language JavaScript so that you can explore the realm of web development.

am having problems with my code for dealing with multiple ball collisions.

The code works absolutely perfect 99% of the time however there appears to be a bug where with certain ball positions, angles and velocities the collision reaction is erratic.

I need this code to be highly optimised as it is for a mobile phone game.

I have spent weeks trying to fix this with the only outcome being my head hurting. When I trace the variable values I can see the problem appears to occur when the value of dt is greater than 1 (see code attached) but I cannot work out how to solve this issue.

Fingers crossed!

The code works absolutely perfect 99% of the time however there appears to be a bug where with certain ball positions, angles and velocities the collision reaction is erratic.

I need this code to be highly optimised as it is for a mobile phone game.

I have spent weeks trying to fix this with the only outcome being my head hurting. When I trace the variable values I can see the problem appears to occur when the value of dt is greater than 1 (see code attached) but I cannot work out how to solve this issue.

Fingers crossed!

```
local function moveBallObjects()
local friction = 0.8
local dV = 0.08
for i = allBallsGroup.numChildren, 1, -1 do
local horizontalVelocity = allBallsGroup[i].horizontalVelocity
local verticalVelocity = allBallsGroup[i].verticalVelocity
local V = (horizontalVelocity * horizontalVelocity) + (verticalVelocity * verticalVelocity)
if V ~= 0 then
V = mSqrt(V)
allBallsGroup[i][2].rotation = allBallsGroup[i][2].rotation + 10 * V
local k = (V - dV) / V
if k < 0 then
k = 0
end
print ("k = " ..k)
allBallsGroup[i].horizontalVelocity = horizontalVelocity * k
allBallsGroup[i].verticalVelocity = verticalVelocity * k
print ("horizontalVelocity = " .. allBallsGroup[i].horizontalVelocity)
print ("verticalVelocity = " .. allBallsGroup[i].verticalVelocity)
allBallsGroup[i].tempX = allBallsGroup[i].tempX + allBallsGroup[i].horizontalVelocity
allBallsGroup[i].tempY = allBallsGroup[i].tempY + allBallsGroup[i].verticalVelocity
for j = allBallsGroup.numChildren, 1, -1 do
if allBallsGroup[i] ~= allBallsGroup[j] then
checkForCollisionWithBall (allBallsGroup[i], allBallsGroup[j])
end
end
allBallsGroup[i].x = allBallsGroup[i].tempX
allBallsGroup[i].y = allBallsGroup[i].tempY
end
end
end
local function checkForCollisionWithBall( firstBallRef, secondBallRef )
-- ------------------
-- Set some constants
-- ------------------
local radius = 14 -- Radius of ball
local radiusSquared = (2 * radius) * (2 * radius) -- Radius squared
-- ---------------------------------------------
-- Calculate distance between balls on both axis
-- ---------------------------------------------
local dx = secondBallRef.tempX - firstBallRef.tempX -- horizontal distance between the 2 balls
local dy = secondBallRef.tempY - firstBallRef.tempY -- vertical distance between the 2 balls
local dxy = (dx * dx) + (dy * dy)
if (dxy < 0.00001) then
print ("dxy less than 0.00001: dxy = " .. dxy)
return
end
-- -----------------------------------------------------------------------------------
-- If the distance squared is less than the radius squared then we have a collision
-- Note this is an optimisation to prevent having to perform a square root calculation
-- -----------------------------------------------------------------------------------
if (dxy < radiusSquared) then
-- --------------------
-- We have a collision!
-- --------------------
-- ------------------------------------------------------------------------------
-- We now perform the square root to calculate the distance between the two balls
-- ------------------------------------------------------------------------------
dxy = mSqrt(dxy)
local cs = dx/dxy
local sc = dy/dxy
-- -----------------------------------------------------------
-- Calculate component of velocity in the direction of (dx,dy)
-- -----------------------------------------------------------
local vp1 = firstBallRef.horizontalVelocity * cs + firstBallRef.verticalVelocity * sc
local vp2 = secondBallRef.horizontalVelocity * cs + secondBallRef.verticalVelocity * sc
local dt = (radius + radius - dxy) / (vp1 - vp2)
firstBallRef.tempX = firstBallRef.tempX - (firstBallRef.horizontalVelocity * dt)
firstBallRef.tempY = firstBallRef.tempY - (firstBallRef.verticalVelocity * dt)
secondBallRef.tempX = secondBallRef.tempX - (secondBallRef.horizontalVelocity * dt)
secondBallRef.tempY = secondBallRef.tempY - (secondBallRef.verticalVelocity * dt)
dx = secondBallRef.tempX - firstBallRef.tempX -- horizontal distance between the 2 balls
dy = secondBallRef.tempY - firstBallRef.tempY -- vertical distance between the 2 balls
local distance = mSqrt(dx * dx + dy * dy)
local ax = dx/distance
local ay = dy/distance
local va1 = (firstBallRef.horizontalVelocity * ax + firstBallRef.verticalVelocity * ay)
local vb1 = (-1 * firstBallRef.horizontalVelocity * ay + firstBallRef.verticalVelocity * ax)
local va2 = (secondBallRef.horizontalVelocity * ax + secondBallRef.verticalVelocity * ay)
local vb2 = (-1 * secondBallRef.horizontalVelocity * ay + secondBallRef.verticalVelocity * ax)
local vaP1 = va1 + (1 + 1) * (va2 - va1)/(1 + 1/1)
local vaP2 = va2 + (1 + 1) * (va1 - va2)/(1 + 1/1)
firstBallRef.horizontalVelocity = vaP1 * ax - vb1 * ay
firstBallRef.verticalVelocity = vaP1 * ay + vb1 * ax
secondBallRef.horizontalVelocity = vaP2 * ax - vb2 * ay
secondBallRef.verticalVelocity = vaP2 * ay + vb2 * ax
firstBallRef.tempX = firstBallRef.tempX + firstBallRef.horizontalVelocity * dt
firstBallRef.tempY = firstBallRef.tempY + firstBallRef.verticalVelocity * dt
secondBallRef.tempX = secondBallRef.tempX + secondBallRef.horizontalVelocity * dt
secondBallRef.tempY = secondBallRef.tempY + secondBallRef.verticalVelocity * dt
checkForCollisionWithBall ( firstBallRef, secondBallRef )
end
end
```

Do more with

EXPERT OFFICE^{®} is a registered trademark of EXPERTS EXCHANGE^{®}

Second, can you explain the lines 144 and 145?

In your example, the vertical speed is such that on the next frame the two balls will be concentric. (124 + (-11) = 113).

I think that dt > 1 when speed is high enough that in one frame the balls "pass through" the other. (if I correctly understood your algorithm. I think you could simplify it....)

also notice the LUA folder with machine flow which has balls that collide.. again all has pascal source but there are lua scripts too which may contain what you need.

This article gives a good description of the potential problems with floating point math. http://en.wikipedia.org/wiki/Floating_point

Assume two circles penetrate by a tiny amount, so dxy is 27.99999995 or something similarly tiny. You do the (radius + radius - dxy) and the result is a very small float, you get loss of accuracy in the exponent. You then divide that by (vp1 - vp2) creating more accuracy problems, dividing a very small number by another possibly very small number.

These problems are within the nature of the method, there are many games that have the same problems. You can't solve it directly, you either handle the problems or change your method.

Possible solutions -

Ignore any value of dt that is unrealistically big. Either set the force to zero or cap it to some maximum value. This also does not produce ideal behaviour but it solves the problem.

Allow the circles to penetrate to a safe depth before forcing them apart. Ignore any collision that is not at a depth where float accuracy is sufficient. This does not produce ideal behaviour but it solves the problem.

Use a different aproach. A more stable method is not allowing the objects to penetrate. Calculate when the objects will collide and modify their position and velocity accordingly.

I was suprised that you could see the objects penetrating visually. Though I think it because your testing dt which is the relative change it depth from one frame to the next. It might be more consistent to test (dxy - radius * 2), the amount of overlap rather than the change in depth.

The passing through behaviour also depends on what you do with the 'too big' value. Assuming that the accuracy problem happens when objects penetrate a very small amount, it may be better to set dt to some small value rather than zero. The objects still have to be pushed apart, setting dt to zero allows the circles to carry on in the same direction until the penetration gets bigger.

You might consider making the collision radius of the circle slightly bigger than its visual radius. If the visual disruption isn't significant, you could just ignore it. It depends on what matters for your game.

There are a few ways to optimise this method that don't invovle the math. The same applies to another method too. If you decide to use the different method I'd appreciate you posting your code as another question. It makes the question/solution business simpler, and experts seem to avoid getting involved in longer threads.

The problem seems to be the normal sort of "omigosh" moments that happen when you do math on a floating point unit.. very small numbers divided by smaller numbers yield big numbers, unexpectedly.

Clamp the value of dt to always be less than 1.. in other words...

if( dt > 0.9999)

then

dt = 0.9999

or whatever safe value you know.

I know it's not "correct", but as I say, it might help you move on to the next problem in your code.

Hope this helps,

-john

(1) For each ball have a Flag

Flag = true => collision detected

Flag = false => no collision detected (initialise all Flags to this)

(2) At each time step

(a) If collision detected and Flag = false calculate new velocity cause of collision and set Flag= true

(b) If collision detected and Flag = true do not apply collision physics (stops re-application of collision physics)

(c) If no collision detected and Flag = true set Flag = false (two objects now apart)

All of this stops the collision physics being applied multiple times for a single collision. Your code is easily changed.

## Premium Content

You need an Expert Office subscription to comment.Start Free Trial