So, I'm trying to create a game using javascript and canvas 2d api (without any external libraries/frameworks). I plan to keep it simple for now. However, while creating it, I realized that I don't understand some concepts and especially how to load my tiled based map (created with tiled editor) efficiently
I understand that for my small project it would be enough but if I wanted to load some bigger tile map in my game loop it could slow down the performance quite a bit. I found a pretty good solution when using the worker thread, but I don't know if that would solve the problem. I was thinking of something that would only load visible pixels on the screen, but again, I don't know if that would work and if it would be an ideal solution
any solution to solve this problem would help me to understand it better and I would be grateful for it
You should load your assets only once, before you need them (so at load is usually a good place).
Then drawing the bitmap with the cropping options of drawImage()
is generally good enough. If your tilemap is so big it's a problem to draw it all like that, you may consider splitting it in multiple smaller files, then it networking becomes an issue, and you are targeting only recent browsers, you can use the createImageBitmap
method which allows to do the cropping directly, and will store only the cropped tile as bitmap, making it a faster asset to use in drawImage
:
(async () => {
const blob = await fetch("https://upload.wikimedia.org/wikipedia/commons/6/68/BOE_tile_set.png")
.then(resp => resp.ok && resp.blob());
const tiles = [];
const tileWidth = 28;
const tileHeight = 36;
for (let i=0; i<77; i++) {
const x = i % 8;
const y = Math.floor(i / 8);
// create one ImageBitmap per tile
const bmp = await createImageBitmap(blob, x * tileWidth, y * tileHeight, tileWidth, tileHeight);
tiles.push({
bmp,
x: 0,
y: 0,
dirX: Math.random() * 2 - 1,
dirY: Math.random() * 2 - 1
});
}
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
draw();
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
tiles.forEach((tile) => {
tile.x = tile.x + tile.dirX;
tile.y = tile.y + tile.dirY;
if (tile.x < -tile.bmp.width) {
tile.x = canvas.width;
}
if (tile.x > canvas.width) {
tile.x = -tile.bmp.width;
}
if (tile.y < -tile.bmp.height) {
tile.y = canvas.height;
}
if (tile.y > canvas.height + tile.height) {
tile.y = -tile.bmp.height;
}
ctx.drawImage(tile.bmp, tile.x, tile.y);
});
requestAnimationFrame(draw);
}
})().catch(console.error);
<canvas></canvas>