移动球体
这是 Gamedev Canvas 教程 中 10 步中的第 2 步。您可以在这里找到完成本课后代码应有的样子:Gamedev-Canvas-workshop/lesson2.html.
您已经知道如何绘制一个球体,这来自之前文章的学习,所以现在让我们让它移动。从技术上讲,我们将把球体绘制在屏幕上,然后清除它,并在每一帧稍微不同的位置重新绘制它,从而产生移动的印象 - 就像电影中的移动一样。
定义绘制循环
为了持续更新每一帧的画布绘制,我们需要定义一个绘制函数,该函数会不断重复执行,每次使用不同的变量值来改变精灵位置等。您可以使用 JavaScript 定时函数来重复执行一个函数。在本教程的后面,我们将看到 requestAnimationFrame()
如何帮助绘制,但我们首先从 setInterval()
开始,以创建一些循环逻辑。
删除您当前 HTML 文件中所有 JavaScript 代码,除了前两行,并在它们下方添加以下代码。draw()
函数将在 setInterval
中每 10 毫秒执行一次
function draw() {
// drawing code
}
setInterval(draw, 10);
由于 setInterval
的无限循环性质,draw()
函数将每 10 毫秒被调用一次,直到我们停止它为止。现在,让我们绘制球体 - 在 draw()
函数中添加以下代码
ctx.beginPath();
ctx.arc(50, 50, 10, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
现在尝试您更新的代码 - 球体应该在每一帧被重新绘制。
使其移动
目前您不会注意到球体持续不断地被重新绘制,因为它没有移动。让我们改变一下。首先,我们不会使用硬编码的 (50,50) 位置,而是使用名为 x
和 y
的变量定义一个起点,位于画布的底部中心部分,然后使用它们来定义绘制圆形的位置。
首先,在 draw()
函数上方添加以下两行代码,以定义 x
和 y
let x = canvas.width / 2;
let y = canvas.height - 30;
接下来更新 draw()
函数,以便在 arc()
方法中使用 x 和 y 变量,如下面的突出显示行所示
function draw() {
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
现在是重要部分:我们想要在每一帧绘制完后,给 x
和 y
添加一个很小的值,以使球体看起来像在移动。让我们将这些很小的值定义为 dx
和 dy
,并将它们的值分别设置为 2 和 -2。在您的 x 和 y 变量定义下方添加以下代码
let dx = 2;
let dy = -2;
最后要做的是在每一帧更新 x
和 y
,使用我们的 dx
和 dy
变量,这样球体就会在每次更新时被绘制到新的位置。在您的 draw()
函数中添加以下两行新代码
function draw() {
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
x += dx;
y += dy;
}
再次保存您的代码并在浏览器中尝试。这可以正常工作,但似乎球体后面留下了痕迹
在每帧之前清除画布
球体留下了痕迹,因为我们在每一帧都绘制了一个新的圆形,而没有删除之前的圆形。别担心,因为有一个清除画布内容的方法:clearRect()
。此方法接受四个参数:矩形左上角的 x 和 y 坐标,以及矩形右下角的 x 和 y 坐标。此矩形覆盖的整个区域将被清除,之前绘制的所有内容都会被删除。
在 draw()
函数中添加以下突出显示的新行代码
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
x += dx;
y += dy;
}
保存您的代码并再次尝试,这次您将看到球体移动而没有留下痕迹。每 10 毫秒,画布就会被清除,蓝色圆形(我们的球体)将被绘制到指定的位置,并且 x
和 y
值将被更新以用于下一帧。
清理我们的代码
在接下来的几篇文章中,我们将向 draw()
函数添加越来越多的命令,所以最好保持它尽可能简洁和干净。让我们从将球体绘制代码移到一个单独的函数开始。
将现有的 draw() 函数替换为以下两个函数
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
x += dx;
y += dy;
}
比较您的代码
您可以在下面的实时演示中查看本文的完成代码,并与它交互,以更好地了解它是如何工作的。
注意:实时样本会自动在这些页面上运行,因此我们添加了一个“开始游戏”按钮。这对于避免游戏自动启动并过于频繁地触发警报或其他事件很有用。
<canvas id="myCanvas" width="480" height="320"></canvas>
<button id="runButton">Start game</button>
canvas {
background: #eee;
}
button {
display: block;
}
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
let x = canvas.width / 2;
let y = canvas.height - 30;
const dx = 2;
const dy = -2;
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
x += dx;
y += dy;
}
function startGame() {
setInterval(draw, 10);
}
document.getElementById("runButton").addEventListener("click", function () {
startGame();
this.disabled = true;
});
注意:尝试更改移动球体的速度,或它移动的方向。