弹跳墙壁
这是 游戏开发 Canvas 教程 的 10 个步骤中的 **第 3 步**。您可以在 Gamedev-Canvas-workshop/lesson3.html 中找到完成本课后的源代码。
看到我们的球在移动真是太好了,但它很快就会从屏幕上消失,限制了我们可以从中获得的乐趣!为了克服这个问题,我们将实现一些碰撞检测(将在 后面 更详细地解释),让球弹离画布的四条边。
简单碰撞检测
为了检测碰撞,我们将检查球是否接触(碰撞)墙壁,如果是,我们将相应地改变它的运动方向。
为了简化计算,让我们定义一个名为 ballRadius
的变量,它将保存绘制圆的半径,并用于计算。将此添加到您的代码中,位于现有变量声明的下方
const ballRadius = 10;
现在更新 drawBall()
函数中绘制球的代码行到下面这样
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
从顶部和底部弹起
有四面墙可以让球弹起——让我们先关注顶部的那面。我们需要在每一帧都检查球是否接触画布的顶边——如果是,我们将反转球的运动,使其开始向相反方向移动,并保持在可见边界内。记住坐标系从左上角开始,我们可以得到这样的代码
if (y + dy < 0) {
dy = -dy;
}
如果球的位置的 y
值小于零,则通过将它设置为自身的反向来改变 y
轴上的运动方向。如果球以每帧 2 个像素的速度向上移动,现在它将以 -2 个像素的速度“向上”移动,这实际上等同于以每帧 2 个像素的速度向下移动。
上面的代码将处理球从顶边弹起,现在让我们考虑一下底边
if (y + dy > canvas.height) {
dy = -dy;
}
如果球的 y
位置大于画布的高度(记住我们从左上角计算 y
值,因此顶边从 0 开始,底边在 320 像素处,即画布的高度),则让它从底边弹起,如前所述反转 y
轴运动。
我们可以将这两个语句合并为一个,以节省代码冗余
if (y + dy > canvas.height || y + dy < 0) {
dy = -dy;
}
如果两个语句中的任何一个为 true
,则反转球的运动。
从左侧和右侧弹起
我们已经覆盖了顶部和底部边缘,所以让我们考虑一下左侧和右侧。实际上非常相似,您只需要重复 x
而不是 y
的语句即可
if (x + dx > canvas.width || x + dx < 0) {
dx = -dx;
}
if (y + dy > canvas.height || y + dy < 0) {
dy = -dy;
}
此时,您应该将上面的代码块插入 draw()
函数中,位于闭合花括号之前。
球仍然会消失到墙里!
在此时测试您的代码,您将会印象深刻——现在我们有一个球可以弹离画布的四条边!但是,我们还有另一个问题——当球撞到每面墙时,它会稍微陷进去,然后再改变方向
这是因为我们正在计算墙的碰撞点和球的中心,而我们应该对球的圆周进行计算。球应该在接触墙后立即弹起,而不是已经深入墙中一半时才弹起,所以让我们稍微调整一下语句来包含这一点。将您添加的最后一段代码更新为以下内容
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
dy = -dy;
}
当球的中心与墙边的距离正好等于球的半径时,它将改变运动方向。从一个边的宽度减去半径,并在另一个边上加上半径,会给我们一种正确的碰撞检测的印象——球按应有的方式弹离墙壁。
比较您的代码
让我们再次检查一下这部分的完成代码与您得到的结果,并玩一玩
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
const ballRadius = 10;
let x = canvas.width / 2;
let y = canvas.height - 30;
let dx = 2;
let dy = -2;
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
dy = -dy;
}
x += dx;
y += dy;
}
function startGame() {
setInterval(draw, 10);
}
document.getElementById("runButton").addEventListener("click", function () {
startGame();
this.disabled = true;
});
注意:尝试在球每次撞到墙时将它的颜色更改为随机颜色。