Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do(es) the ball(s) stick to the ground after an arbitrary amount of time?

I have the following code which bounces multiple balls along the screen, making use of physics, and the gravity of jupiter divided by 10 for testing purposes.

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext('2d');

//jupiter's gravity divided by 10 for testing purposes
g = 24.79/10;
canvas.width = window.innerWidth - 50;
canvas.height = window.innerHeight - 22.5;
bounciness = (1/2)
var spawnrate = 16;
var inertia = 0.00075;
var gravity = g/25;
players = []
then = new Date()/1000;
moved = false;
function getPosition(event){
	mouseX = event.clientX;
	mouseY = event.clientY;
	if(moved == false){
		update();
		moved = true;
	}
}
function addCircle(){
    players.push({x: mouseX, y: mouseY, color: '#000000', radius: 10, velY: 0, velX: 2, jumped: false, jump: 0.02, max: 0});
}
first = new Date();
function update(){
        ctx.clearRect(0, 0, canvas.width, canvas.height);
	t = new Date() - first;
	now = new Date() / 1000;
	if(now-then >= 1/spawnrate){
		then = now;
		addCircle();
	}
	for(var c = 0; c < players.length; c++){
		circle = players[c];
		circle.y+=circle.velY;
		circle.x+=circle.velX;
		circle.velX-=inertia;
		circle.velY+=gravity;
		if(Math.abs(circle.velY) > Math.abs(circle.max)){
			circle.max = circle.velY;
		}
		if(circle.y + circle.radius/2 > canvas.height){
			circle.velY*=-Math.sqrt(bounciness);
		}
		updateCircle(circle);
		if(circle.x > canvas.width){
			players.splice(c, 1);
		}

	}
        setTimeout(update, 10);
}

function drawLine(x1, y1, x2, y2){
	ctx.beginPath();
	ctx.moveTo(x1, y1);
	ctx.lineTo(x2, y2);
	ctx.stroke();
}

function updateCircle(player){
        ctx.fillStyle = player.color;
        ctx.beginPath();
        ctx.arc(player.x, player.y, player.radius, 0, Math.PI * 2);
        ctx.fill();
}

window.addEventListener("mousemove", getPosition, false);
if(moved == true){
	update();
}
#canvas{
    display: block;
}
<!DOCTYPE html>
<html>
	<head>
		<title>Bounce</title>
	</head>
	<body>
		<canvas id="canvas"></canvas>
	</body>
</html>

If after running the code snippet you don't see the error, try moving the mouse around the screen arbitrarily.

However, after I move the mouse around, I notice that some of the balls seem to stick to the bottom of the screen.

enter image description here

I have been working on this for the past 8 hours, only to be dumbfounded. The code is relatively simple, so I don't think it's any obvious mistake.

By the way, I am going to add a +500 bounty in 2 days

like image 299
user1823 Avatar asked Mar 16 '23 00:03

user1823


1 Answers

Your problem is the collison detection:

if (circle.y + circle.radius/2 > canvas.height) {
    circle.velY*=-Math.sqrt(bounciness);
}

This just inverts the velocity, but does not adjust the position. Which means that when you drop your balls from certain heights, they can get stuck in the ground - they have entered it by a certain depth, but in the next frame their reduced velocity is no more enough to take them out of the ground, and gets inverted right again taking them a bit deeper in the ground…

You can solve this fairly easy by making sure that their vertical position never gets "negative", or even completely bounce the ball away from the ground:

var inGround = circle.y + circle.radius - canvas.height;
if (inGround >= 0) {
    circle.velY *= -Math.sqrt(bounciness);
    circle.y -= 2*inGround;
}

Try the updated snippet demo:

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext('2d');

//jupiter's gravity divided by 10 for testing purposes
g = 24.79/10;
canvas.width = window.innerWidth - 50;
canvas.height = window.innerHeight - 22.5;
bounciness = (1/2)
var spawnrate = 16;
var inertia = 0.00075;
var gravity = g/25;
players = []
then = new Date()/1000;
moved = false;
function getPosition(event){
	mouseX = event.clientX;
	mouseY = event.clientY;
	if(moved == false){
		update();
		moved = true;
	}
}
function addCircle(){
    players.push({x: mouseX, y: mouseY, color: '#000000', radius: 10, velY: 0, velX: 2, jumped: false, jump: 0.02, max: 0});
}
first = new Date();
function update(){
        ctx.clearRect(0, 0, canvas.width, canvas.height);
	t = new Date() - first;
	now = new Date() / 1000;
	if(now-then >= 1/spawnrate){
		then = now;
		addCircle();
	}
	for(var c = 0; c < players.length; c++){
		circle = players[c];
		circle.y+=circle.velY;
		circle.x+=circle.velX;
		circle.velX-=inertia;
		circle.velY+=gravity;
		if(Math.abs(circle.velY) > Math.abs(circle.max)){
			circle.max = circle.velY;
		}
        var inGround = circle.y + circle.radius - canvas.height;
		if(inGround >= 0){
			circle.velY *= -Math.sqrt(bounciness);
            circle.y -= 2*inGround;
		}
		updateCircle(circle);
		if(circle.x > canvas.width){
			players.splice(c, 1);
		}

	}
        setTimeout(update, 10);
}

function drawLine(x1, y1, x2, y2){
	ctx.beginPath();
	ctx.moveTo(x1, y1);
	ctx.lineTo(x2, y2);
	ctx.stroke();
}

function updateCircle(player){
        ctx.fillStyle = player.color;
        ctx.beginPath();
        ctx.arc(player.x, player.y, player.radius, 0, Math.PI * 2);
        ctx.fill();
}

window.addEventListener("mousemove", getPosition, false);
if(moved == true){
	update();
}
#canvas{
    display: block;
}
<!DOCTYPE html>
<html>
	<head>
		<title>Bounce</title>
	</head>
	<body>
		<canvas id="canvas"></canvas>
	</body>
</html>
like image 108
Bergi Avatar answered Apr 25 '23 17:04

Bergi