0

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

anathrax
  • 91
  • 1
  • 1
  • 11
  • Are you familiar with Leaflet? – Barry Carter Jul 14 '22 at 14:15
  • No but I also forgot to mention that I prefer to do things myself without external libraries/frameworks if possible. But thanks for the recommendation :D – anathrax Jul 14 '22 at 14:21
  • `I prefer to do things myself without external libraries/frameworks`. I won't be sarcastic and say something like "then just develop Javascript directly from assembly language" or something, but, the more complicated something becomes, the harder it is to do it without libraries (or by essentially rewriting libraries) – Barry Carter Jul 14 '22 at 14:23
  • yes I completely understand you but I think that to understand the principle it is better to start like this – anathrax Jul 14 '22 at 14:31
  • 1
    OK, good luck. You might want to add "no external libraries" to your question. – Barry Carter Jul 14 '22 at 14:34
  • Without actual code sample it’s really hard to help you optimize. – mati.o Jul 14 '22 at 14:40

1 Answers1

0

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>
Kaiido
  • 123,334
  • 13
  • 219
  • 285