模拟球在 Canvas 边界弹跳

Simulating Balls Bouncing in Canvas Boundary

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var h = canvas.height;
var w = canvas.width;
var sAngle = 0;
var numB = 10; 
var speed = 50;
var dt = 0.01;
const PI = Math.PI;
function resetCanvas () {
  canvas.width = canvas.width;
};
function createBalls (){
for(var i = 1; i <= numB; i++){ 
if (i % 2 == 0) {
  window['ball' + i] = 
  {r:10, color:"white", x:w*Math.random(), y:h*Math.random(), v:speed}} else {
   window['ball' + i] = 
  {r:10, color:"white", x:w*Math.random(), y:h*Math.random(), v:-1 *speed}};
      }
    }
  createBalls();
function drawBalls () {
for (var i = 1; i <= numB; i++) {
ctx.beginPath();
ctx.arc(window['ball' + i].x, window['ball' + i].y, window['ball' + i].r, sAngle, 2*PI);
ctx.fillStyle = window['ball' + i].color;
ctx.fill();
ctx.strokeStyle = window['ball' + i].color;
ctx.stroke();
    }
}
drawBalls();
function moveBalls () {
for (var i = 1; i <= numB; i++) {
if (0 < window['ball' + i].x < w && 0 < window['ball' + i].y < h) 
{window['ball' + i].x = window['ball' + i].x + window['ball' + i].v * dt; 
window['ball' + i].y = window['ball' + i].y + window['ball' + i].v * dt}
if (window['ball' + i].x < 0 || window['ball' + i].x > w) 
{window['ball' + i].x = window['ball' + i].x + ((-1) * window['ball' + i].v * dt); 
window['ball' + i].y = window['ball' + i].y + window['ball' + i].v * dt}
if (window['ball' + i].y < 0 || window['ball' + i].y > h) 
{window['ball' + i].y = window['ball' + i].y + ((-1) * window['ball' + i].v * dt);
window['ball' + i].x = window['ball' + i].x + window['ball' + i].v * dt}
    }
}
function animate () {
resetCanvas();
drawBalls();
moveBalls();
};
setInterval(animate, 100 * dt);

我试图让球从 canvas 墙上向相反的方向反弹;但是,现在它们刚好碰到 canvas 边界并滑到角落并消失了。关于如何改进我的 moveBall 函数中的 if 条件以便球从 canvas 墙上反弹的任何想法?

不确定这是否是您唯一的问题,但您的 if 语句没有达到您的预期。例如:

if (0 < window["ball" + i].x < w && 0 < window["ball" + i].y < h)

这里你好像想说0 < ball.x < w,意思是ball.x在零和屏幕宽度之间。问题是这不是比较的工作方式。你需要说:

if (0 < window["ball" + i].x && window["ball" + i].x < w && 
    0 < window["ball" + i].y && window["ball" + i].y < h)

原因是表达式 0 < x < w(0 < x) < w 一样执行,第一部分 (0 < x) returns 为真 (1) 或假 (0),并且这两个都可能小于 w 所以表达式总是 true.

例如,试试这个片段:

let x = 7;
console.log("Nope:",x,"is bewteen 0 and 6:",0 < x < 6);
console.log("Yep! ",x,"is between 0 and 6:",0 < x && x < 6); 

所以尝试这样写 moveBalls

function moveBalls() {
  for (var i = 1; i <= numB; i++) {
    // precompute values used multiple times for easier reading
    const ball = window["ball" + i];
    const offset = ball.v * dt;
    if (0 < ball.x && ball.x < w && 0 < ball.y && ball.y < h) {
      ball.x = ball.x + offset;
      ball.y = ball.y + offset;
    }
    if (ball.x < 0 || ball.x > w) {
      ball.x = ball.x - offset;
      ball.y = ball.y + offset;
    }
    if (ball.y < 0 || ball.y > h) {
      ball.y = ball.y - offset;
      ball.x = ball.x + offset;
    }
  }
}

除了 之外,边缘处理不正确。我猜你想让他们反弹?

我们来看代码:

if (window['ball' + i].x < 0 || window['ball' + i].x > w) {
  window['ball' + i].x = window['ball' + i].x + ((-1) * window['ball' + i].v * dt);
  window['ball' + i].y = window['ball' + i].y + window['ball' + i].v * dt
}

让我们摆脱一些噪音:

if (x < 0 || x > w) {
  x = x + ((-1) * v * dt);
  y = y + v * dt
}

-1 * v 的判断是正确的,但这仍然只会再次将球移动到 canvas 的边界内。接下来循环它会在边界内,正常动作,它的速度会再次增加,再次放在边界外,再次触发这段代码。这实际上 "sticks" 球到了边界。当它碰到一个边界时,另一个坐标仍然移动,直到那个坐标也超出边界。所有的球最终都在 0,0 附近或 canvas.

的对角线上

你在这里要做的是使速度相反:

if (x < 0 || x > w || y < 0 || y > h) {
  v *= -1
}

这会导致球向相反的方向反弹。

尽管如此,这段代码仍然不是很完美,因为实际上速度具有维度分量,即 x 速度和 y 速度。

代码使用局部变量更具可读性,尤其是使用球数组。稍后我可能会回来对速度进行维度化,但现在的代码是这样的:

const canvas = document.getElementById("canvas");
const height = canvas.clientHeight;
const width = canvas.clientWidth;
canvas.height = height;
canvas.width = width;
const ctx = canvas.getContext("2d");
const h = canvas.height;
const w = canvas.width;
const sAngle = 0;
const numB = 10;
const speed = 50;
const dt = 0.01;
const PI = Math.PI;
const balls = createBalls();

function resetCanvas() {
  canvas.width = canvas.width;
}

function createBalls() {
  const balls = [];
  for (let i = 1; i <= numB; i++) {
    const ball = createBall();
    if (i % 2 == 0) {
      ball.v *= -1;
    }
    balls.push(ball);
  }
  return balls;
}

function createBall() {
  return { r: 10, color: "white", x: w * Math.random(), y: h * Math.random(), v: speed };
}

function drawBalls() {
  balls.forEach(ball => {
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.r, sAngle, 2 * PI);
    ctx.fillStyle = ball.color;
    ctx.fill();
    ctx.strokeStyle = ball.color;
    ctx.stroke();
  });
}

function moveBalls() {
  balls.forEach(ball => {
    const { x, y } = ball;

    if (x < 0 || x > w || y < 0 || y > h) {
      ball.v *= -1
    }
    ball.x = x + ball.v * dt;
    ball.y = y + ball.v * dt
  });
}
function animate() {
  resetCanvas();
  drawBalls();
  moveBalls();
};
setInterval(animate, 100 * dt);