rmpstmp
rmpstmp

Reputation: 281

Detection and response ball-to-wall collision inside any polygon

Need to code good method for detection and response ball-to-wall collision inside any polygon.

For example, I have a method which draw a ball which fly inside a rectangle.

ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();

Detect and response that collision very simple.

if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) {
    dx = -dx;
}

if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) {
    dy = -dy;
}

But I have a polygon: variable with positions (x and y) of each point.

var polygonPoints = [
{
    x: 240,
    y: 30
},
{
    x: 140,
    y: 100
},
{
    x: 180,
    y: 250
},
{
    x: 320,
    y: 280
},
{
    x: 400,
    y: 50
}

];

And function wich draw my polygon:

ctx.beginPath();
ctx.strokeStyle = '#333';
ctx.moveTo(polygonPoints[0].x, polygonPoints[0].y);
for (var i = 1, n = polygonPoints.length; i < n; i++) {
    ctx.lineTo(polygonPoints[i].x, polygonPoints[i].y);
}
ctx.lineTo(polygonPoints[0].x, polygonPoints[0].y);
ctx.stroke();
ctx.closePath();

How I can detect and response collisions inside a polygon?

Demo on jsfiddle.

var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var ballRadius = 10;
var x = canvas.width/2;
var y = canvas.height-30;
var dx = 2;
var dy = -2;
var polygonPoints = [
	{
    	x: 240,
        y: 30
    },
    {
    	x: 140,
        y: 100
    },
    {
    	x: 180,
        y: 250
    },
    {
    	x: 320,
        y: 280
    },
    {
    	x: 400,
        y: 50
    }
];

function drawBall() {
    ctx.beginPath();
    ctx.arc(x, y, ballRadius, 0, Math.PI*2);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

function drawPolygon() {
    ctx.beginPath();
    ctx.strokeStyle = '#333';
    ctx.moveTo(polygonPoints[0].x, polygonPoints[0].y);
	for (var i = 1, n = polygonPoints.length; i < n; i++) {
    	ctx.lineTo(polygonPoints[i].x, polygonPoints[i].y);
    }
    ctx.lineTo(polygonPoints[0].x, polygonPoints[0].y);
    ctx.stroke();
    ctx.closePath();
}

function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drawBall();
    drawPolygon();
    
    if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) {
        dx = -dx;
    }
    if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) {
        dy = -dy;
    }
    
    x += dx;
    y += dy;
    window.requestAnimationFrame(draw);
}

draw();
canvas {
    border: 1px solid #333;
}
<canvas id="myCanvas" width="480" height="320"></canvas>

Upvotes: 2

Views: 2110

Answers (3)

markE
markE

Reputation: 105015

Here's how to test for circle (ball) collisions) versus any line in your polygon.

First, calculate the closes point on a line relative to your ball:

function calcClosestPtOnSegment(x0,y0,x1,y1,cx,cy){

    // calc delta distance: source point to line start
    var dx=cx-x0;
    var dy=cy-y0;

    // calc delta distance: line start to end
    var dxx=x1-x0;
    var dyy=y1-y0;

    // Calc position on line normalized between 0.00 & 1.00
    // == dot product divided by delta line distances squared
    var t=(dx*dxx+dy*dyy)/(dxx*dxx+dyy*dyy);

    // calc nearest pt on line
    var x=x0+dxx*t;
    var y=y0+dyy*t;

    // clamp results to being on the segment
    if(t<0){x=x0;y=y0;}
    if(t>1){x=x1;y=y1;}

    return({ x:x, y:y, isOnSegment:(t>=0 && t<=1) });
}

Second, test if the ball is close enough to collide with that line like this:

var dx=ballX-nearestX;
var dy=ballY-nearestY
var isColliding=(dx*dx+dy*dy<ballRadius*ballRadius);

Finally, if the ball collided with that side, calculate the ball's reflection angle (== its outgoing angle):

Here's an illustration of the angles involved in the calculation:

  • The red line indicates the ball's incoming angle.
  • The gold line indicates the ball's outgoing angle.
  • The outgoing angle equals the incoming angle plus twice the diff-angle.

enter image description here

And here's some pseudo-code showing how to do the calculation:

var wallNormalAngle = wallAngle-PI/2; // assuming clockwise angle calculations
var differenceAngle = incidenceAngle - wallNormalAngle;
var reflectionAngle = incidenceAngle + 2 * differenceAngle

Upvotes: 4

user151496
user151496

Reputation: 1985

this is a broad question. there are many methods to calculate 2d collisions. luckily, circle collisions are quite simple, as it's exactly the same size on each size

you will most likely want to work with movement vectors (dx/dy) and line vectors. try to calculate following movement one step ahead on each iteration and see where the vectors intersect. then calculate necessary forces

needless to say, mathematics and trigonometry computations are necessary. it's not difficult, just people usually get scared when needing to use sinuses and cosinuses

there is a very very good explaining article on this topic from the maker of the N physics engine http://www.metanetsoftware.com/technique/tutorialA.html

Upvotes: 0

Oisin
Oisin

Reputation: 781

I may have misread your question, I will update my answer shortly.

Since it's a polygon you only have a finite number of verts(corners) and in the case of a collision with the edge of the screen at least one of the corners will make contact. This means you only need to loop through the list of Polygone points and check if [any] are outside of the screen.

Upvotes: 0

Related Questions