1
\$\begingroup\$

I'm trying to make a ball bounce off some obstacles after a bounding box + pixel by pixel collision. Somehow, sometimes the ball is not going in the direction it is supposed to be.

If the ball is following the red arrow, it is supposed to follow the green arrow after that. What happens is that sometimes it just goes back around (orange arrow). The same happens when going up on this wall, or even in the left wall.

The "bug"

Here is the collision and reflection code

isPixelCollision(element) {

    var x = Math.round(this.pos.x),
        y = Math.round(this.pos.y),
        x2 = Math.round(element.pos.x),
        y2 = Math.round(element.pos.y);

    var w = this.width,
        h = this.height,
        w2 = element.width,
        h2 = element.height;



    // find the top left and bottom right corners of overlapping area
    var xMin = Math.max(x, x2),
        yMin = Math.max(y, y2),
        xMax = Math.min(x + w, x2 + w2),
        yMax = Math.min(y + h, y2 + h2);

    // Sanity collision check, we ensure that the top-left corner is both
    // above and to the left of the bottom-right corner.
    if (xMin >= xMax || yMin >= yMax) {
        return false;
    }

    var xDiff = xMax - xMin,
        yDiff = yMax - yMin;

    // get the pixels out from the images
    var pixels = this.imgData.data,
        pixels2 = element.imgData.data;




    // if the area is really small,
    // then just perform a normal image collision check
    if (xDiff < 4 && yDiff < 4) {
        for (var pixelX = xMin; pixelX < xMax; pixelX++) {
            for (var pixelY = yMin; pixelY < yMax; pixelY++) {
                if ((pixels[((pixelX - x) + (pixelY - y) * w) * 4 + 3] !== 0) && (pixels2[((pixelX - x2) + element.width * (element.actual - 1) + (pixelY - y2) * w2 * element.total) * 4 + 3] !== 0)) {
                    console.log("Small Collision\n");
                    var vector = {
                        x: (pixelX - Math.floor(this.pos.x + (this.width / 2))),
                        y: -(pixelY - Math.floor(this.pos.y + (this.height / 2)))
                    };
                    if (element.constructor.name != 'Light')
                        this.angle = (Math.atan2(vector.y, vector.x) - Math.PI) * (180 / Math.PI);
                    else {
                        this.speed += 50;

                    }
                    return true;
                }
            }
        }
    } else {

        var incX = xDiff / 3.0,
            incY = yDiff / 3.0;
        incX = (~~incX === incX) ? incX : (incX + 1 | 0);
        incY = (~~incY === incY) ? incY : (incY + 1 | 0);

        for (var offsetY = 0; offsetY < incY; offsetY++) {
            for (var offsetX = 0; offsetX < incX; offsetX++) {
                for (var pixelY = yMin + offsetY; pixelY < yMax; pixelY += incY) {
                    for (var pixelX = xMin + offsetX; pixelX < xMax; pixelX += incX) {
                        if ((pixels[((pixelX - x) + (pixelY - y) * w) * 4 + 3] !== 0) && (pixels2[((pixelX - x2) + element.width * (element.actual - 1) + (pixelY - y2) * w2 * element.total) * 4 + 3] !== 0)) {

                            var vector = {
                                x: (pixelX - Math.floor(this.pos.x + (this.width / 2))),
                                y: -(pixelY - Math.floor(this.pos.y + (this.height / 2)))
                            };

                            console.log("Big Collision");

                            if (element.constructor.name != 'Light')
                                this.angle = (Math.atan2(vector.y, vector.x) - Math.PI) * (180 / Math.PI);
                            else {
                                if (this.speed < 500) this.speed += 50;

                            }
                            return true;
                        }
                    }
                }
            }
        }
    }

    return false;
}

This is a more mathematical way of doing it that I tried, but works even worse than the one before

//Normal Vector
var normal = {
    x: (pixelX - (this.imgData.width / 2)),
    y: -(pixelY - (this.imgData.height / 2))
};
//Normalize the vetor
var norm = Math.sqrt(normal.x * normal.x + normal.y * normal.y);
if (norm != 0) {
    normal.x = normal.x / norm;
    normal.y = normal.y / norm;
}
var n_rad = Math.atan2(normal.y, normal.x);
var n_deg = (n_rad + Math.PI) * 180 / Math.PI;
console.log("Vetor Normal -> (" + normal.x + ", " + normal.y + ") , Angulo: " + n_deg);
//Velocity vector
var velocity = {
    x: Math.cos((this.angle * Math.PI / 180) - Math.PI),
    y: Math.sin((this.angle * Math.PI / 180) - Math.PI)
};
console.log("Vetor Velocidade -> (" + velocity.x + ", " + velocity.y + ") , Angulo: " + this.angle);
//Vetor Reflexao
var ndotv = normal.x * velocity.x + normal.y * velocity.y;
var reflection = {
    x: -2 * ndotv * normal.x + velocity.x,
    y: -2 * ndotv * normal.y + velocity.y
};
var r_rad = Math.atan2(reflection.y, reflection.x);
var r_deg = (r_rad + Math.PI) * 180 / Math.PI;
console.log("Vetor Reflexao -> (" + reflection.x + ", " + reflection.y + ") , Angulo: " + r_deg);
this.angle = r_deg;
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

What you need to do is to calculate the normal of the collision then reflect the impact vel across the collision normal: // r = v - 2 * dot(v, n) * n; r = result vel, v = impact vel, n = normal of collision.

\$\endgroup\$
2
  • \$\begingroup\$ Yeah, you're right, I kinda know the theory. How do i calculate the r, the v and the n, that's the big question for me. dot(v,n) I assume i do it like this var ndotv = normal.x * velocity.x + normal.y * velocity.y; \$\endgroup\$ Commented May 17, 2016 at 10:31
  • \$\begingroup\$ r is what you want, vel after bounce, vel is wat the obj vel is before collision. n you get from collision detection. For example, in your picture n is (-1,0). \$\endgroup\$
    – user80667
    Commented May 17, 2016 at 21:26
0
\$\begingroup\$

You have to first check for collision. To do this, you'll need to check your ball's position with the walls:

//check collision with top wall
if (ball.y <= wall.topY + BALL_RADIUS) {
    ball.y = wall.topY + BALL_RADIUS; //change position to the point of collision
    velocity.y = -velocity.y; //reverse the velocity only on the Y axis
}

//check collision with right wall
if (ball.x >= wall.rightX + BALL_RADIUS) {
    ball.x = wall.rightX + BALL_RADIUS; //change position to the point of collision
    velocity.x = -velocity.x; //reverse the velocity only on the X axis
}

//check collision with bottom wall
if (ball.y >= wall.bottomY - BALL_RADIUS) {
    ball.y = wall.bottomY - BALL_RADIUS; //change position to the point of collision
    velocity.y = -velocity.y; //reverse the velocity only on the Y axis
}

//check collision with left wall
if (ball.x <= wall.leftX + BALL_RADIUS) {
    ball.x = wall.leftX + BALL_RADIUS; //change position to the point of collision
    velocity.x = -velocity.x; //reverse the velocity only on the X axis
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .