模拟球在 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);
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);