我正在编写一个简单的游戏来学习 JS,并且在此过程中我正在学习 HTML5,所以我需要在画布上绘制东西。
这是代码:
let paddle = new Paddle(GAME_WIDTH,GAME_HEIGHT);
new InputHandler(paddle);
let lastTime = 0;
const ball = new Image();
ball.src = 'assets/ball.png';
function gameLoop(timeStamp){
let dt = timeStamp - lastTime;
lastTime = timeStamp;
ctx.clearRect(0,0,600,600);
paddle.update(dt);
paddle.draw(ctx);
ball.onload = () => {
ctx.drawImage(ball,20,20);
}
window.requestAnimationFrame(gameLoop);
}
gameLoop();
现在我注释掉了
clearRect()
:
你好球。
画布底部还有一个桨,似乎不受
clearRect()
方法的影响。它工作得很好。我在这里缺少什么?
将图像的
onload
处理程序放入游戏循环中没有多大意义。这意味着游戏必须在设置图像的 onload
功能之前开始运行,从而导致非常混乱的情况。
正确的顺序是设置
onload
处理程序,然后设置图像源,然后 await
所有图像 onload
在运行游戏循环之前触发。当您只有一张图像时,直接将主循环设置为 onload
非常容易,但对于具有多个资产的游戏,这很快就会变得尴尬。
Promise.all
加载许多游戏资源的最小示例。很可能,您希望将加载的图像解压缩为更具描述性的对象而不是数组,但这只是一个开始。
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.width = 400;
canvas.height = 250;
const ctx = canvas.getContext("2d");
const assets = [
"http://placekitten.com/120/100",
"http://placekitten.com/120/120",
"http://placekitten.com/120/140",
];
const assetsLoaded = assets.map(url =>
new Promise((resolve, reject) => {
const img = new Image();
img.onerror = e => reject(`${url} failed to load`);
img.onload = e => resolve(img);
img.src = url;
})
);
Promise
.all(assetsLoaded)
.then(images => {
(function gameLoop() {
requestAnimationFrame(gameLoop);
ctx.clearRect(0, 0, canvas.width, canvas.height);
images.forEach((e, i) =>
ctx.drawImage(
e,
i * 120, // x
Math.sin(Date.now() * 0.005) * 20 + 40 // y
)
);
})();
})
.catch(err => console.error(err));