为什么球会在任意时间后粘在地上?

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

我有以下代码,它利用物理原理和木星引力除以 10 来沿屏幕弹起多个球。

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>

如果在 运行 代码片段之后您没有看到错误,请尝试在屏幕上任意移动鼠标。

但是,当我移动鼠标后,我发现有些球似乎粘在屏幕底部。

过去8个小时我一直在研究这个,结果傻眼了。代码比较简单,所以我觉得没有什么明显的错误。

顺便说一下,我将在 2 天内添加 +500 赏金

你的问题是碰撞检测:

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

这只是反转速度,而不是调整位置。这意味着当您从特定高度掉落球时,它们可能会卡在 地面 - 它们已进入地面一定深度,但在下一帧中它们不再降低速度足以将它们带出地面,然后再次向右倒转,使它们在地下更深一点……

您可以很容易地解决这个问题,方法是确保他们的垂直位置永远不会达到 "negative",甚至不会将球完全弹离地面:

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

试试更新后的片段演示:

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>