# Pong - Collision Detection

Sat, May 14, 2011

## Ball Movement

In order to have some challenge, we need the ball to accelerate. Given a position, a speed, an acceleration and a time interval dt, the new position and speed of the ball can be calculated as follows:

``````accelerate: function(x, y, dx, dy, accel, dt) {
var x2  = x + (dt * dx) + (accel * dt * dt * 0.5);
var y2  = y + (dt * dy) + (accel * dt * dt * 0.5);
var dx2 = dx + (accel * dt) * (dx > 0 ? 1 : -1);
var dy2 = dy + (accel * dt) * (dy > 0 ? 1 : -1);
return { nx: (x2-x), ny: (y2-y), x: x2, y: y2, dx: dx2, dy: dy2 };
},
``````

Once we can accelerate, the ball’s `update()` method needs to:

• calculate the new position and speed of the ball.
• detect if it bounced off the top or bottom wall (simple bounds check)
• detect if it bounced off a paddle (see next section)
• tweak the y speed to simulate spin if the ball hit a moving paddle
``````update: function(dt, leftPaddle, rightPaddle) {

pos = Pong.Helper.accelerate(this.x, this.y, this.dx, this.dy, this.accel, dt);

if ((pos.dy > 0) && (pos.y > this.maxY)) {
pos.y = this.maxY;
pos.dy = -pos.dy;
}
else if ((pos.dy < 0) && (pos.y < this.minY)) {
pos.y = this.minY;
pos.dy = -pos.dy;
}

var pt     = Pong.Helper.ballIntercept(this, paddle, pos.nx, pos.ny);

if (pt) {
switch(pt.d) {
case 'left':
case 'right':
pos.x = pt.x;
pos.dx = -pos.dx;
break;
case 'top':
case 'bottom':
pos.y = pt.y;
pos.dy = -pos.dy;
break;
}

pos.dy = pos.dy * (pos.dy < 0 ? 0.5 : 1.5);
pos.dy = pos.dy * (pos.dy > 0 ? 0.5 : 1.5);
}

this.setpos(pos.x,  pos.y);
this.setdir(pos.dx, pos.dy);
},
``````

The `ballIntercept()` method used in the `update()` method above needs to accurately detect if the ball will intercept with the paddle during this time frame interval (dt).

The ball moves from p1 to p2 during the time interval (dt) and the paddle edge stretches from p3 to p4 and we need to see if these line segments intersect.

We want to be able to see if the ball intercepts the obvious side of the bat.

• If the ball is moving left, check the right edge of player 1’s bat.
• If the ball is moving right, check the left edge of player 2’s bat.

But we also want to check the non-obvious sides, such as the top and bottom to see if the ball ‘just missed’ the obvious edge but will bounce off the top or bottom before going out for a goal. Most pong games dont bother with this, but we are trying to be complete, so, assuming the existence of a simple line `intercept()` method, `ballIntercept()` can be implemented as follows:

``````ballIntercept: function(ball, rect, nx, ny) {
var pt;
if (nx < 0) {
pt = Pong.Helper.intercept(ball.x, ball.y, ball.x + nx, ball.y + ny,
"right");
}
else if (nx > 0) {
pt = Pong.Helper.intercept(ball.x, ball.y, ball.x + nx, ball.y + ny,
"left");
}
if (!pt) {
if (ny < 0) {
pt = Pong.Helper.intercept(ball.x, ball.y, ball.x + nx, ball.y + ny,
"bottom");
}
else if (ny > 0) {
pt = Pong.Helper.intercept(ball.x, ball.y, ball.x + nx, ball.y + ny,
"top");
}
}
return pt;
}
``````

## Line Intersection

The `ballIntercept()` method relies on a generic line segment intersect method.

Assuming the ball moves from (x1,y1) to (x2,y2) and the paddle edge goes from (x3,y3) to (x4,y4), the `intercept()` method can be implemented as follows:

``````intercept: function(x1, y1, x2, y2, x3, y3, x4, y4, d) {
var denom = ((y4-y3) * (x2-x1)) - ((x4-x3) * (y2-y1));
if (denom != 0) {
var ua = (((x4-x3) * (y1-y3)) - ((y4-y3) * (x1-x3))) / denom;
if ((ua >= 0) && (ua <= 1)) {
var ub = (((x2-x1) * (y1-y3)) - ((y2-y1) * (x1-x3))) / denom;
if ((ub >= 0) && (ub <= 1)) {
var x = x1 + (ua * (x2-x1));
var y = y1 + (ua * (y2-y1));
return { x: x, y: y, d: d};
}
}
}
return null;
},
``````

The game is now playable as a 2 player game with this demo here

## More…

You can find the final game here and the code is here