0
\$\begingroup\$

I'm new to html canvas.

So far I am following this code as base for simple isometric tiles.

I made an empty isometric grid map that only shows the grid.

I want to offset the position of my isometric map, down and right and vice versa.

The code that I am using as an example has a little map, with a few tiles and its a fixed position, in my code, I made 10x bigger. I want to navigate through my 100 tiles, navigate to the edges.

This is my code

In the example code there is a function renderTiles, where it has two function inside that has renderY + 48.

renderTileBackground and renderTileHover

When I increase these values to offset an certain axis,the function renderTileHover get buggy, it hovers the wrong tile.

I did follow these instructions for isometric, but I am so lost in this canvas thing.

How do I zoom in and out and pan this isometric canvas grid?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Please share code within the body of the question itself, not linked on an external site where it could change or go offline independent of the question. If you have too much code to fit in the question, then you have some work to do first to pare it down to a Minimal Complete Verifiable Example. \$\endgroup\$
    – DMGregory
    Sep 25, 2022 at 2:05

1 Answer 1

0
\$\begingroup\$

I finally got the right solution, the problem was that there were some variables where the values were fixed and also the values from it was from the code that I was following as reference. It was mismatching with my added logic.

In my old code in the render() I had the following:

 const render = () => {
  tileStartX = canvas.width / 2 - 50;
  tileStartY = canvas.height / 2 - 200;
  ctx.fillStyle = "#151d26";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  renderTiles(tileStartX, tileStartY);
};

Reading this article that I was using also as reference. I changed my code to the following:

 ...some code
 let tile_height = 50;
 let tile_width = 100;

 const render = () => {
  let tile_half_width = tile_width / 2;
  let tile_half_height = tile_height / 2;
  tileStartX = canvas.width / 2 - tile_half_width + viewport.x;
  tileStartY = canvas.height / 2 - tile_half_height + viewport.y;
  ctx.fillStyle = "#151d26";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  renderTiles(tileStartX, tileStartY);
};

Now I want to share my code, to help anyone who wants to pan in the isometric grid in canvas. This is a basic code of isometric grid with pan to any directions. Use the arrow keys for it.

<!-- Code ref
https://codepen.io/zerratar/pen/GMjYyW -->
<template>
  <div id="page-container">
    <div class="top-nav">
      <button class="btn btn-outline-info" id="reset-view">Reset view</button>
    </div>
    <canvas class="le-canvas position-absolute"></canvas>
  </div>
</template>
<script setup>
import { onMounted, reactive, ref } from "vue";
onMounted(() => {
  setTimeout(() => {
    let tile_height = 50;
    let tile_width = 100;
    let resetView = document.getElementById("reset-view");
    let tileMap = [];
    let gridSize = 20;
    let canvas = document.querySelector(".le-canvas");
    let ctx = canvas.getContext("2d");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    let mousePosition = { x: 0, y: 0 };
    let viewport = { x: 0, y: 0 };
    let kbd = {
      ctrl: false,
      u: false,
      d: false,
      l: false,
      r: false,
    };
    // Solution below ref from https://gamedev.stackexchange.com/questions/167038/canvas-infinite-map-how-to-have-reappearing-objects-at-static-x-y-even-when-loo?rq=1
    // Code ref for panning the map https://jsfiddle.net/n8d5kbau/
    document.addEventListener("keydown", (e) => {
      if (e.keyCode === 37) {
        kbd.l = true;
        e.preventDefault();
      } else if (e.keyCode === 38) {
        kbd.u = true;
        e.preventDefault();
      } else if (e.keyCode === 39) {
        kbd.r = true;
        e.preventDefault();
      } else if (e.keyCode === 40) {
        kbd.d = true;
        e.preventDefault();
      } else if (e.keyCode === 32) {
        kbd.space = true;
        e.preventDefault();
      }
    });
    document.addEventListener("keyup", (e) => {
      if (e.keyCode === 37) {
        kbd.l = false;
        e.preventDefault();
      } else if (e.keyCode === 38) {
        kbd.u = false;
        e.preventDefault();
      } else if (e.keyCode === 39) {
        kbd.r = false;
        e.preventDefault();
      } else if (e.keyCode === 40) {
        kbd.d = false;
        e.preventDefault();
      } else if (e.keyCode === 32) {
        kbd.space = false;
        e.preventDefault();
      }
    });

    if (resetView) {
      resetView.onclick = () => {
        viewport = { x: 0, y: 0 };
      };
    }

    let hoverTileX = -1;
    let hoverTileY = -1;

    let tileStartX = canvas.width / 2 - tile_width / 2 + viewport.x;
    let tileStartY = canvas.height / 2 - tile_height / 2 + viewport.y;

    var isMouseDown = false;

    const getMousePos = (canvas, evt) => {
      var rect = canvas.getBoundingClientRect();
      return {
        x: evt.clientX - rect.left,
        y: evt.clientY - rect.top,
      };
    };
    const renderMouseAndGridPosition = () => {
      let mouse_over_grid =
        hoverTileX >= 0 &&
        hoverTileY >= 0 &&
        hoverTileY <= gridSize &&
        hoverTileX <= gridSize
          ? `Grid: ${hoverTileX}, ${hoverTileY}`
          : "";
      ctx.font = "12pt Calibri";
      ctx.fillStyle = "white";
      ctx.fillText(`Mouse: ${mousePosition.x}, ${mousePosition.y}`, 20, 100);
      ctx.fillText(`${mouse_over_grid}`, 20, 120);
    };
    const render = () => {
      let tile_half_width = tile_width / 2;
      let tile_half_height = tile_height / 2;
      tileStartX = canvas.width / 2 - tile_half_width + viewport.x;
      tileStartY = canvas.height / 2 - tile_half_height + viewport.y;
      ctx.fillStyle = "#151d26";
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      renderTiles(tileStartX, tileStartY);
      renderMouseAndGridPosition();
    };

    const renderTileHover = (x, y, width, height) => {
      ctx.beginPath();
      ctx.setLineDash([]);
      ctx.strokeStyle = "rgba(192, 57, 43, 0.8)";
      ctx.fillStyle = "rgba(192, 57, 43, 0.4)";
      ctx.lineWidth = 2;
      ctx.moveTo(x, y);
      ctx.lineTo(x + width / 2, y - height / 2);
      ctx.lineTo(x + width, y);
      ctx.lineTo(x + width / 2, y + height / 2);
      ctx.lineTo(x, y);
      ctx.stroke();
      ctx.fill();
    };
    const renderTiles = (x, y) => {
      let tileWidth = tile_width;
      let tileHeight = tile_height;
      let tile_half_width = tileWidth / 2;
      let tile_half_height = tileHeight / 2;

      if (kbd) {
        if (kbd.l) {
          viewport.x += 3;
        }
        if (kbd.r) {
          viewport.x -= 3;
        }
        if (kbd.u) {
          viewport.y += 3;
        }
        if (kbd.d) {
          viewport.y -= 3;
        }
      }
      // if (viewport.x > 0) viewport.x = -canvas.width;
      // if (viewport.y > 0) viewport.y = -canvas.height;
      // if (viewport.x < -canvas.width) viewport.x = 0;
      // if (viewport.y < -canvas.height) viewport.y = 0;

      for (let tileX = 0; tileX < gridSize; ++tileX) {
        for (let tileY = 0; tileY < gridSize; ++tileY) {
          let renderX = x + (tileX - tileY) * tile_half_width;
          let renderY = y + (tileX + tileY) * tile_half_height;
          let tile = tileMap[tileY * gridSize + tileX];
          //   if(tile !== tiletype_empty) renderTexturedTile(tile_images[tile], renderX, renderY, 80);
          //   else renderTileBackground(renderX, renderY+50, tileWidth, tileHeight);

          renderTileBackground(renderX, renderY + 50, tileWidth, tileHeight);
        }
      }

      if (
        hoverTileX >= 0 &&
        hoverTileY >= 0 &&
        hoverTileX < gridSize &&
        hoverTileY < gridSize
      ) {
        let renderX = x + (hoverTileX - hoverTileY) * tile_half_width;
        let renderY = y + (hoverTileX + hoverTileY) * tile_half_height;

        renderTileHover(renderX, renderY + 50, tileWidth, tileHeight);
      }
    };

    const renderTileBackground = (x, y, width, height) => {
      ctx.beginPath();
      ctx.setLineDash([5, 5]);
      ctx.strokeStyle = "rgba(255,255,255,0.4)";
      ctx.fillStyle = "rgba(25,34, 44,0.2)";
      ctx.lineWidth = 1;
      ctx.moveTo(x, y);
      ctx.lineTo(x + width / 2, y - height / 2);
      ctx.lineTo(x + width, y);
      ctx.lineTo(x + width / 2, y + height / 2);
      ctx.lineTo(x, y);
      ctx.stroke();
      ctx.fill();
    };

    const update = (elapsed) => {
      let mouse_y = mousePosition.y - tileStartY;
      let mouse_x = mousePosition.x - tileStartX;

      hoverTileX = Math.floor(mouse_y / tile_height + mouse_x / tile_width) - 1;
      hoverTileY = Math.floor(-mouse_x / tile_width + mouse_y / tile_height);

      if (isMouseDown === true) {
        if (
          hoverTileX >= 0 &&
          hoverTileY >= 0 &&
          hoverTileX < gridSize &&
          hoverTileY < gridSize
        ) {
          var tileIndex = hoverTileY * gridSize + hoverTileX;
          //   if (tileIndex < tileMap.length) {
          //     let tileType = selectedTileType; // (tileMap[tileIndex] + 1) % tile_textures.length;
          //     tileMap[tileIndex] = tileType;
          //   }
        }
      }
    };

    canvas.addEventListener(
      "mousemove",
      (evt) => {
        mousePosition = getMousePos(canvas, evt);
      },
      false
    );

    const run = (e) => {
      update(e);
      render();
      window.requestAnimationFrame(run);
    };
    run();
  }, 1000);
});
</script>
<style lang="scss">
body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  background: black;
}

#page-container {
  position: relative;
  min-height: 100vh;
}


.top-nav {
  position: absolute;
  top: 0;
  width: 100%;
  height: 10vh;
  z-index: 5;
  background-color: rgba(0, 0, 0, 0.349);
}

</style>
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .