0

I created a game based on Mozilla Developer's breakout game, but at a certain moment when the ball collides with the paddle, it zig zags inside it and comes out. How can I solve this problem? Here is my javascript code:

const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
const ballRadius = 10;
const paddleHeight = 10;
const paddleWidth = 75;
let paddleX = (canvas.width - paddleWidth) / 2;
let rightPressed = false;
let leftPressed = false;
let x = canvas.width / 2;
let y = canvas.height - 30;
let dx = 2;
let dy = -2;

let paddleY = canvas.height - paddleHeight;

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

function drawPaddle() {
    ctx.beginPath();
    ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

function draw() {
    ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
    drawBall();
    drawPaddle();

    // Colisão da bola no eixo X
    if (x + dx < 0 + ballRadius || x + dx > canvas.width - ballRadius) {
        dx = -dx
    }

    // Colisão eixo Y
    if (y + dy < 0 + ballRadius) {
        dy = -dy
    }


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

    if (y + dy < ballRadius) {
        dy = -dy
    } else if (y + dy > canvas.height - (ballRadius * 2)) {
        if (x > paddleX && x < paddleX + paddleWidth) {
            dy = -dy
        } else if (y + ballRadius > canvas.height) {
            alert("a")
            document.location.reload();
            clearInterval(interval)
        }
    }

    if (rightPressed) {
        paddleX += 7;
        if (paddleX + paddleWidth > canvas.width) {
            paddleX = canvas.width - paddleWidth
        }
    } else if (leftPressed) {
        paddleX -= 7
        if (paddleX < 0) {
            paddleX = 0
        }
    }

    x += dx;
    y += dy;

}

// Eventos de controle via teclado
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

function keyDownHandler(e) {
    if (e.key === "Right" || e.key === "ArrowRight") {
        rightPressed = true;
    } else if (e.key === "Left" || e.key === "ArrowLeft") {
        leftPressed = true;
    }
}

function keyUpHandler(e) {
    if (e.key === "Right" || e.key === "ArrowRight") {
        rightPressed = false;
    } else if (e.key === "Left" || e.key === "ArrowLeft") {
        leftPressed = false;
    }
}

const interval = setInterval(draw, 10);

This is a gif with the problem: Animation with the bug

I try different solutions but not resolve my error.

trincot
  • 317,000
  • 35
  • 244
  • 286

1 Answers1

0

This happens when the ball hits the user's paddle first at the side, so that it is already well below the top of the paddle when it first hits it. When the vertical direction is flipped, the next iteration is not enough to bring the ball above the paddle -- it is still below its top. And the problem occurs: the vertical direction is flipped again!

I sized the canvas so that it occurs immediately on the first bounce (provided the user does not move the paddle):

const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
const ballRadius = 10;
const paddleHeight = 10;
const paddleWidth = 75;
let paddleX = (canvas.width - paddleWidth) / 2;
let rightPressed = false;
let leftPressed = false;
let x = canvas.width / 2;
let y = canvas.height - 30;
let dx = 2;
let dy = -2;

let paddleY = canvas.height - paddleHeight;

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

function drawPaddle() {
    ctx.beginPath();
    ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

function draw() {
    ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
    drawBall();
    drawPaddle();

    // Colisão da bola no eixo X
    if (x + dx < 0 + ballRadius || x + dx > canvas.width - ballRadius) {
        dx = -dx
    }

    // Colisão eixo Y
    if (y + dy < 0 + ballRadius) {
        dy = -dy
    }


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

    if (y + dy < ballRadius) {
        dy = -dy
    } else if (y + dy > canvas.height - (ballRadius * 2)) {
        if (x > paddleX && x < paddleX + paddleWidth) {
            dy = -dy;
        } else if (y + ballRadius > canvas.height) {
            console.log("lost! reloading")
            document.location.reload();
            clearInterval(interval)
        }
    }

    if (rightPressed) {
        paddleX += 7;
        if (paddleX + paddleWidth > canvas.width) {
            paddleX = canvas.width - paddleWidth
        }
    } else if (leftPressed) {
        paddleX -= 7
        if (paddleX < 0) {
            paddleX = 0
        }
    }

    x += dx;
    y += dy;

}

// Eventos de controle via teclado
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

function keyDownHandler(e) {
    if (e.key === "Right" || e.key === "ArrowRight") {
        rightPressed = true;
    } else if (e.key === "Left" || e.key === "ArrowLeft") {
        leftPressed = true;
    }
}

function keyUpHandler(e) {
    if (e.key === "Right" || e.key === "ArrowRight") {
        rightPressed = false;
    } else if (e.key === "Left" || e.key === "ArrowLeft") {
        leftPressed = false;
    }
}

const interval = setInterval(draw, 10);
<canvas id="myCanvas" width="192" height="100" style="background: silver"></canvas>

The fix is straightforward: don't flip the vertical direction dy, but force it to be negative, like this:

dy = -Math.abs(dy);

If it already was negative, then it wont be flipped again.

Here is the same code as above, but with the fix applied:

const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
const ballRadius = 10;
const paddleHeight = 10;
const paddleWidth = 75;
let paddleX = (canvas.width - paddleWidth) / 2;
let rightPressed = false;
let leftPressed = false;
let x = canvas.width / 2;
let y = canvas.height - 30;
let dx = 2;
let dy = -2;

let paddleY = canvas.height - paddleHeight;

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

function drawPaddle() {
    ctx.beginPath();
    ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight);
    ctx.fillStyle = "#0095DD";
    ctx.fill();
    ctx.closePath();
}

function draw() {
    ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
    drawBall();
    drawPaddle();

    // Colisão da bola no eixo X
    if (x + dx < 0 + ballRadius || x + dx > canvas.width - ballRadius) {
        dx = -dx
    }

    // Colisão eixo Y
    if (y + dy < 0 + ballRadius) {
        dy = -dy
    }


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

    if (y + dy < ballRadius) {
        dy = -dy
    } else if (y + dy > canvas.height - (ballRadius * 2)) {
        if (x > paddleX && x < paddleX + paddleWidth) {
            dy = -Math.abs(dy);  // <---- fix!
        } else if (y + ballRadius > canvas.height) {
            console.log("lost! reloading")
            document.location.reload();
            clearInterval(interval)
        }
    }

    if (rightPressed) {
        paddleX += 7;
        if (paddleX + paddleWidth > canvas.width) {
            paddleX = canvas.width - paddleWidth
        }
    } else if (leftPressed) {
        paddleX -= 7
        if (paddleX < 0) {
            paddleX = 0
        }
    }

    x += dx;
    y += dy;

}

// Eventos de controle via teclado
document.addEventListener("keydown", keyDownHandler, false);
document.addEventListener("keyup", keyUpHandler, false);

function keyDownHandler(e) {
    if (e.key === "Right" || e.key === "ArrowRight") {
        rightPressed = true;
    } else if (e.key === "Left" || e.key === "ArrowLeft") {
        leftPressed = true;
    }
}

function keyUpHandler(e) {
    if (e.key === "Right" || e.key === "ArrowRight") {
        rightPressed = false;
    } else if (e.key === "Left" || e.key === "ArrowLeft") {
        leftPressed = false;
    }
}

const interval = setInterval(draw, 10);
<canvas id="myCanvas" width="192" height="100" style="background: silver"></canvas>
trincot
  • 317,000
  • 35
  • 244
  • 286
  • Now it is correct, but there is this case where the ball enters the racket diagonally. [Imgur](https://imgur.com/2cpdd7k) – Rafael Neves Jul 30 '23 at 15:03
  • Not sure what you mean. Can you provide the canvas size for which that happens without moving? – trincot Jul 30 '23 at 15:46
  • It was happened when the canvas size is 500 width and 500 height – Rafael Neves Jul 30 '23 at 16:26
  • I don't see what the problem is. I ran it on 500x500 canvas [here](https://jsfiddle.net/8n29tvea/) -- what do you mean with "this case where the ball enters the racket diagonally"? It does exactly the same as in your code, except that this wobbling on the racket is now no longer happening. Please clarify. – trincot Jul 30 '23 at 18:59
  • When the ball enter on the racket diagonally, she didnt collide. – Rafael Neves Aug 25 '23 at 00:20
  • That's what you said a month ago, but did you read my reply? Please provide the exact details on how to reproduce the problem you see. Provide a fiddle (like I did in my previous comment) that shows what you mean. When you run the snippet in my answer you see that the ball hits the racket diagonally and bounces. – trincot Aug 25 '23 at 05:38