Ball collision not working - javascript

I'm working on a JQuery / JavaScript canvas brick break game, so I added the collision detection code if the ball hits the paddle or not, but when I ran the code, the collision detection code didn't work, I was wondering if anyone could help me? Here's the code.
JSFiddle: https://jsfiddle.net/18h7b9fd/
Main.js
$(document).ready(() => {
let fps = 60;
setInterval(init, 1000 / fps);
$(canvas).bind("mousemove", paddleControl);
})
// INIT
let init = () => {
draw(); //draw objects
animate(); //animate objects
collision(); //detect collision
}
// DRAW
let draw = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
paddle.draw();
ball.draw();
}
// ANIMATE
let animate = () => {
ball.animate();
}
// COLLISION
let collision = () => {
ball.collision();
}
// CONTROL
let paddleControl = (e) => {
// get mouse pos
let rect = canvas.getBoundingClientRect();
let root = document.documentElement;
let mouseX = e.pageX - rect.left - root.scrollLeft;
paddle.x = mouseX - paddle.w / 2;
}
Objects.js
class Paddle {
constructor(x, y, w, h, color) {
this.x = canvas.width / 2 - 100 / 2;
this.y = canvas.height - 60;
this.w = 100;
this.h = 10;
this.color = "#fff";
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
}
class Ball {
constructor(x, y, r, color, speedX, speedY) {
this.x = canvas.width / 2 - 10 / 2;
this.y = canvas.height / 2 - 10 / 2;
this.r = 10;
this.color = "#fff";
this.speedX = 6;
this.speedY = 6;
}
draw() {
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill();
}
animate() {
this.x += this.speedX;
this.y += this.speedY;
}
collision() {
// BALL TO PADDLE
let paddleTop = paddle.y - paddle.h;
let paddleBottom = canvas.height - paddleTop;
let paddleLeft = paddle.x;
let paddleRight = paddle.x + paddle.w;
if(this.y >= paddleTop && this.y <= paddleBottom &&
this.x >= paddleLeft && this.x <= paddleRight) {
this.speedY *= -1;
}
// BALL TO WALL
if(this.x >= canvas.width) this.speedX *= -1; //left
if(this.x <= 0) this.speedX *= -1; //right
if(this.y >= canvas.height) this.reset(); //bottom
if(this.y <= 0) this.speedY *= -1; //top
}
reset() {
this.x = canvas.width / 2 - this.r / 2;
this.y = canvas.height / 2 - this.r / 2;
this.animate();
}
}
let paddle = new Paddle(this.x, this.y, this.w, this.h, this.color);
let ball = new Ball(this.x, this.y, this.r, this.color, this.speedX, this.speedY);
console.log(paddle);
console.log(ball);

The problem is the way you are setting the paddle top and bottom. Use this instead:
let paddleTop = paddle.y;
let paddleBottom = paddleTop + paddle.h;
Currently the way you are setting the values makes it impossible for the condition to ever be true (as paddle bottom is always less than paddle top).
I have put together a fiddle here: https://jsfiddle.net/gegbqv5p/
You will also notice that I didn't use jQuery. For what you have so far, it really isn't necessary.

Related

Why isn't my rectangle showing up in HTML Canvas?

I am currently trying to build a Digital Table Tennis game and I have a lot in place so far, but whenever I run the program, the non-AI player's bat (The green rectangle) isn't showing up. It doesn't seem to be there at all. Is there some bug in my code? Also make sure to mainly pay attention to the bottom of the code, where the animate() function is declared. The outcome so far is here: https://Digital-Table-Tennis.programprodigy.repl.co. PLS HELP!
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
canvas.width = innerWidth;
canvas.height = innerHeight;
let mouse = {
x: null
}
window.onmousemove = e => {
mouse.x = e.x;
}
class Ball {
constructor() {
this.dx = Math.random() * 4 - 2;
this.dy = Math.random() * 4 - 2;
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;
}
draw() {
ctx.fillStyle = 'orange';
ctx.beginPath();
ctx.arc(this.x, this.y, 10, 0, Math.PI * 2);
ctx.fill();
}
update() {
this.x += this.dx;
this.y += this.dy;
if (
this.x >= mouse.x &&
this.x <= mouse.x + 50 &&
this.y >= canvas.height - 5 &&
this.y <= canvas.height
) {
this.dx = -this.dx;
this.dy = -this.dy;
}
if (
this.x <= 0 ||
this.x >= canvas.width ||
this.y <= 0 ||
this.y >= canvas.height
) {
this.dx = -this.dx;
this.dy = -this.dy;
}
this.draw();
}
}
let ball = new Ball();
class AI {
constructor(lvl) {
this.x = 0;
this.color = 'red';
this.speed = lvl * 5;
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, 0, 50, 5);
}
update() {
if (ball.y < canvas.height / 2) {
let distance = ball.x - this.x;
this.x += distance / this.speed;
}
if (
ball.x >= this.x &&
ball.x <= this.x + 50 &&
ball.y >= 0 &&
ball.y <= 5
) {
ball.dx = -ball.dx;
ball.dy = -ball.dy;
}
this.draw();
}
}
let ai_player = new AI(5);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'red';
ctx.fillRect(mouse.x - 25, canvas.height - 5, 50, 5);
ball.update();
ai_player.update();
requestAnimationFrame(animate);
}
animate();
I actually have a red player rectangle on my end, however I had to scroll down to see it since the canvas height was more than my viewport.
You specified that the player rectangle should be green, but it's declared in animate() as ctx.fillStyle = 'red';.
As for solving the scrolling issues, it should be a quick CSS fix by specifying a max-width of 100vw and a max-height of 100vh on your canvas element, assuming you don't have any conflicting styles.

How to check for collision on canvas for objects created from the same class?

I am building a ball physics simulator in Javascript. I understand how to detect collisions for individually created objects, but struggle when the objects are constructed using ES6 classes. Here is the code I have so far.
Ball Constructor
const Ball = class {
constructor(x, y, dx, dy) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
}
checkForWall() {
if (this.x + ballRadius < ballRadius) {
this.dx = -this.dx;
}
if (this.y + ballRadius < ballRadius) {
this.dy = -this.dy;
}
if (this.x + ballRadius > canvas.width) {
this.dx = -this.dx;
}
if (this.y + ballRadius > canvas.height) {
this.dy = -this.dy;
}
}
moveBall() {
this.x += this.dx;
this.y += this.dy;
}
};
Ball Drawing Function on Canvas
const drawBalls = function (x, y) {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = `green`;
ctx.fill();
ctx.closePath();
};
Drawing Function on canvas with interval timer
const draw = function () {
ctx.clearRect(0, 0, canvas.width, canvas.clientHeight);
drawBalls(ballOne.x, ballOne.y);
drawBalls(ballTwo.x, ballTwo.y);
ballTwo.moveBall();
ballTwo.checkForWall();
ballOne.moveBall();
ballOne.checkForWall();
}
let interval = setInterval(draw, 10);
Is there a way to implement code in the above class to check whether or not balls constructed from this class have collided with other balls constructed from the same class in the same way that I am able to check for collision with the canvas walls? Do I need to create a parent class and a child class?

Check if mouse is in or out of window

I have seen that this type of question has already been answered, however every answer is a little vague or has some cross-compatibility issues or is too beyond my understanding.So is there anyone who can give my an up-to-date, full,clear (showing and explaining all the steps involved) answer?I would like not to make the circles grow in size when the mouse is outside the window.I am not using JQuery(featured in many answers).Should I use it?Is there any way to implement this with pure javascript?
Thank you for your help and sorry if my code is a little bit redundant :)
var canvas = document.querySelector("canvas");
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var mouse = {
x: undefined,
y: undefined
}
window.addEventListener("mousemove", function(event){
mouse.x = event.x;
mouse.y = event.y;
})
window.addEventListener("resize", function(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
})
var colorArray = [
"#6CC61D",
"#E05132",
"#278E83",
"#A633D4"
]
function Circle(x,y,dx,dy,radius){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.color = colorArray[Math.floor(Math.random() * colorArray.length)];
this.draw = function(){
c.beginPath();
c.arc(this.x,this.y,this.radius,0,Math.PI*2,false);
c.fillStyle = this.color;
c.fill()
}
this.update = function(){
if (this.x + this.radius>window.innerWidth || this.x -this.radius<0){
this.dx = - this.dx;}
if (this.y + this.radius > window.innerHeight || this.y - this.radius < 0) {
this.dy = - this.dy;
}
if (mouse.x - this.x < 50 && mouse.x - this.x > - 50 && mouse.y - this.y < 50 && mouse.y - this.y > - 50 && this.radius<50 ){
this.radius += 1;
}
else if(this.radius>2){
this.radius -= 1;
}
this.x += this.dx;
this.y += this.dy;
this.draw();
}
}
var circleArray = [];
for (var i = 0; i < 600; i++) {
var x = Math.random() * (window.innerWidth - radius * 2) + radius;
var y = Math.random() * (window.innerHeight - radius * 2) + radius;
var dy = (Math.random() - 0.5) * 8;
var dx = (Math.random() - 0.5) * 8;
var radius = 30;
circleArray.push(new Circle(x, y, dx, dy, radius));
}
function animate(){
requestAnimationFrame(animate);
c.clearRect(0,0,window.innerWidth,window.innerHeight);
for (var i = 0;i < circleArray.length;i++){
circleArray[i].update()
}
circle.update();
}
animate();
I am not using JQuery (featured in many answers). Should I use it ?
Is there any way to implement this with pure javascript ?
It's according to your preferences. As JQuery is javascript, if you can do it with JQuery, you can do it with JS only. For what is about "Is it implementable ?", most likely. I will start to format a bit your code to decouple the logic (calculs/updating positions, speed & radius) from the rendering (drawing), it will be easier to operate.
Checking if the mouse is out of window :
You can use the mouseout event to detect when the mouse leave the window
window.addEventListener('mouseout', action);
Avoid the circles to grow in size when the mouse is outside the window :
For the action, set back the mouse.x & mouse.y to undefined
window.addEventListener('mouseout', function(){
mouse.x = undefined;
mouse.y = undefined;
})
and then add 2 more conditions in your Circle update method to only grow the radius if, in addition of the others conditions, mouse.x & mouse.y are defined (so if the mouse is in the window).
if (mouse.x &&
mouse.y &&
Math.abs(mouse.x - this.x) < 50 &&
Math.abs(mouse.y - this.y) < 50 &&
this.radius < 50 ) {
this.radius += 1;
}
Result :
const canvas = document.querySelector("canvas");
const c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const mouse = {
x: undefined,
y: undefined
}
window.addEventListener("mousemove", function(event){
mouse.x = event.x;
mouse.y = event.y;
})
window.addEventListener("resize", function(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
})
window.addEventListener('mouseout', function(){
mouse.x = undefined;
mouse.y = undefined;
})
const colorArray = [
"#6CC61D",
"#E05132",
"#278E83",
"#A633D4"
]
function Circle(x, y, dx, dy, radius){
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.color = colorArray[Math.floor(Math.random() * colorArray.length)];
this.draw = function(){
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
c.fillStyle = this.color;
c.fill()
}
this.update = function(){
if (this.x + this.radius > window.innerWidth || this.x - this.radius < 0) {
this.dx = - this.dx;
}
if (this.y + this.radius > window.innerHeight || this.y - this.radius < 0) {
this.dy = - this.dy;
}
if ( mouse.x &&
mouse.y &&
Math.abs(mouse.x - this.x) < 50 &&
Math.abs(mouse.y - this.y) < 50 &&
this.radius < 50 ) {
this.radius += 1;
} else if(this.radius > 2) {
this.radius -= 1;
}
this.x += this.dx;
this.y += this.dy;
}
}
const circleArray = [];
for (let i = 0; i < 600; i++) {
const radius = 30;
const x = Math.random() * (window.innerWidth - radius * 2) + radius;
const y = Math.random() * (window.innerHeight - radius * 2) + radius;
const dy = (Math.random() - 0.5) * 8;
const dx = (Math.random() - 0.5) * 8;
circleArray.push(new Circle(x, y, dx, dy, radius));
}
function clearCanvas(){
c.clearRect(0,0,window.innerWidth,window.innerHeight);
}
function update(){
for (let i = 0; i < circleArray.length; i++){
circleArray[i].update()
}
}
function draw(){
for (let i = 0; i < circleArray.length; i++){
circleArray[i].draw()
}
}
function animate(){
requestAnimationFrame(animate);
clearCanvas();
update();
draw();
}
requestAnimationFrame(animate);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<canvas id="signature-pad" class="signature-pad" width=400 height=200></canvas>
</body>
</html>

clearTimeout not stopping canvas animation

So I have a simple canvas animation I want to start and stop using a toggle switch. The animation works fine and starts up, but the clearTimeout does not seem to work.
I made sure the variable "timeOut" is defined globally so it should have access to it, that seemed to be the usual advice in past questions similar to this one. Am I make a mistake somewhere else? Tried it on Firefox, Chrome and Chromium.
let timeOut;
function circles() {
let canvas = document.querySelector('canvas');
canvas.width = document.querySelector('canvas').offsetWidth;
canvas.height = document.querySelector('canvas').offsetHeight;
let c = canvas.getContext('2d');
let topRadius = 30;
let colorArray = [
'#ff0000',
'#00ff00',
'#0000ff',
]
let mouse = {
x: undefined,
y: undefined
}
window.addEventListener('mousemove', function(e) {
mouse.x = e.x;
mouse.y = e.y;
})
window.addEventListener('resize', function() {
canvas.width = document.querySelector('canvas').offsetWidth;
canvas.height = document.querySelector('canvas').offsetHeight;
init();
});
function Circle(x, y, dx, dy, radius) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dx;
this.radius = radius;
this.minRadius = radius;
this.color = colorArray[Math.floor(Math.random() * colorArray.length)];
this.draw = function() {
if (this.radius < 0) {
this.radius = this.minRadius;
}
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
c.fillStyle = this.color;
c.fill();
}
this.update = function() {
if (this.y + this.radius > innerHeight || this.y - this.radius <= 0) {
this.dy = -this.dy;
}
if (this.x + this.radius > canvas.width|| this.x - this.radius <= 0) {
this.dx = -this.dx;
}
this.x+=this.dx;
this.y+=this.dy;
if (mouse.x - this.x < 50 && mouse.x - this.x > -50 && mouse.y - this.y < 50 && mouse.y - this.y > -50) {
if (this.radius < topRadius) {
this.radius += 1;
}
} else if (this.radius > this.minRadius){
this.radius -= 1;
}
this.draw();
}
}
let circleArray = [];
function init() {
circleArray = [];
for (let i =0; i<50; i++) {
let radius = Math.random() * 10;
let x = Math.random() * (canvas.width - radius*2) + radius;
let y = Math.random() * (canvas.height - radius * 2) + radius;
let dx = (Math.random() - 0.5) * 4;
let dy = (Math.random() - 0.5 )* 4;
circleArray.push(new Circle(x, y, dx, dy, radius));
}
}
function animate() {
requestAnimationFrame(animate);
c.clearRect(0,0, canvas.width, innerHeight);
for(let i=0; i < circleArray.length; i++) {
circleArray[i].update();
}
}
init();
animate();
}
$('#startstop').click(function() {
if(!timeOut) {
timeOut = setTimeout(circles, 1000);
} else {
clearTimeout(timeOut);
timeOut = null;
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="display: flex; justify-content: space-around;">
<canvas style="width: 100px; height: 800px; background: red;"></canvas>
<div id="startstop">toggle</div>
</div>
You were super close to the solution ;)
in the function animate add line #3 (before for loop), that is having this value:
if (!timeOut) return;
Good luck!
This animation is not stopping because you call clearTimeout on your setTimeout but that only runs once. Your issue is further into the animate function. This relatively loops the requestAnimationFrame. You can store this as a request and then cancelAnimationFrame

How to make multiple balls drop on click?

I have a ball that drops from cursor location, and redrops when the cursor is moved to another location. I am trying get a new ball to drop every time I click the mouse. I tried:
canvas.addEventListener('click', function(event) {
ball.draw();
});
But it doesn't seem to do anything. Is there some way to draw a NEW ball on click instead of just redrawing the same ball over and over again?
Here's the rest of the code:
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d");
var W = window.innerWidth,
H = window.innerHeight;
var running = false;
canvas.height = H; canvas.width = W;
var ball = {},
gravity = .5,
bounceFactor = .7;
ball = {
x: W,
y: H,
radius: 15,
color: "BLUE",
vx: 0,
vy: 1,
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
};
function clearCanvas() {
ctx.clearRect(0, 0, W, H);
}
function update() {
clearCanvas();
ball.draw();
ball.y += ball.vy;
ball.vy += gravity;
if(ball.y + ball.radius > H) {
ball.y = H - ball.radius;
ball.vy *= -bounceFactor;
}
}
canvas.addEventListener("mousemove", function(e){
ball.x = e.clientX;
ball.y = e.clientY;
ball.draw();
});
setInterval(update, 1000/60);
ball.draw();
Just rewrite the ball object so it becomes instantiate-able:
function Ball(W, H) {
this.x = W;
this.y = H;
this.radius = 15;
this.color = "blue";
this.vx = 0;
this.vy = 1;
}
Move the methods to prototypes (this will make them shareable across instances). In addition, add an update method so you can localize updates:
Ball.prototype = {
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
},
update: function() {
this.y += this.vy;
this.vy += gravity;
if(this.y + this.radius > H) {
this.y = H - this.radius;
this.vy *= -bounceFactor;
}
}
};
In the click event (consider renaming the array to plural form - it's easier to distinguish that way. In your code you're overriding the "array" (which is defined as an object) with a single ball object later):
var balls = []; // define an array to hold the balls
For the click event to use the x and y position of the mouse as start point for the ball, we first need to adjust it as it is relative to client window and not the canvas. To do this we get the absolute position of canvas and subtract it from the client coordinates:
canvas.addEventListener('click', function(event) {
var rect = this.getBoundingClientRect(), // adjust mouse position
x = event.clientX - rect.left,
y = event.clientY - rect.top;
balls.push(new Ball(x, y)); // add a new instance
});
Now in the main animation loop just iterate over the array. Every time there is a new ball it will be considered and updated - we just let the loop run until some condition is met (not shown):
function update() {
clearCanvas();
for(var i = 0, ball; ball = balls[i]; i++) {
ball.draw(); // this will draw current ball
ball.update(); // this will update its position
}
requestAnimationFrame();
}
Live example
If you put these together you will get:
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
W = canvas.width, // simplified for demo
H = canvas.height,
gravity = .5,
bounceFactor = .7;
function Ball(x, y) {
this.x = x;
this.y = y;
this.radius = 15;
this.color = "blue";
this.vx = 0;
this.vy = 1
}
Ball.prototype = {
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
},
update: function() {
this.y += this.vy;
this.vy += gravity; // todo: limit bounce at some point or this value will be added
if (this.y + this.radius > H) {
this.y = H - this.radius;
this.vy *= -bounceFactor;
}
}
};
function clearCanvas() {
ctx.clearRect(0, 0, W, H);
}
var balls = []; // define an array to hold the balls
canvas.addEventListener('click', function(event) {
var rect = this.getBoundingClientRect(), // adjust mouse position
x = event.clientX - rect.left,
y = event.clientY - rect.top;
balls.push(new Ball(x, y)); // add a new instance
});
(function update() {
clearCanvas();
for (var i = 0, ball; ball = balls[i]; i++) {
ball.draw(); // this will draw current ball
ball.update(); // this will update its position
}
requestAnimationFrame(update);
})();
canvas {background:#aaa}
<canvas id="canvas" width=600 height=400></canvas>

Categories