I'm writing one of those simple games to learn JS and I'm learning HTML5 in the process so I need to draw things on canvas.
Here's the code:
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();
screenshot: no ball before comment
now I comment out the clearRect()
:
hello ball.
There's also a paddle at the bottom of the canvas that doesn't seem to be affected by the clearRect()
method. It works just fine. What am I missing here?
It doesn't make much sense to put the image's onload
handler inside the game loop. This means the game has to begin running before the image's onload
function is set, leading to a pretty confusing situation.
The correct sequence is to set the onload
handlers, then the image sources, then await
all of the image onload
s firing before running the game loop. Setting the main loop to an onload
directly is pretty easy when you only have one image, but for a game with multiple assets, this can get awkward quickly.
Here's a minimal example of how you might load many game assets using Promise.all
. Very likely, you'll want to unpack the loaded images into more descriptive objects rather than an array, but this is a start.
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.width = 400;
canvas.height = 250;
const ctx = canvas.getContext("2d");
const assets = [
"https://picsum.photos/120/100",
"https://picsum.photos/120/120",
"https://picsum.photos/120/140",
];
const assetsLoaded = assets.map(async url => {
const img = new Image();
img.src = url;
await img.decode();
return img;
});
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));