基本思想来自游戏地图。根据我的代码审查,该地图是整页画布。我在画布上绘制图像没有问题。我的问题是如何检测地图房屋并更新画布,甚至为其添加点击功能。我附上了原始游戏中的 GIF 和 HTML 代码,以便更好地理解我的请求。

<div id="canvasBorder"><canvas id="canvasMap"></canvas></div>

好的,这是我的代码。这很简单。我根据画布上很大的主图像绘制了地图上的房屋。

function onClick2() {
  const imagePath = '/lobby/map.png';

  //Image Positions and Width/Height
  const array = [
    { x: 1764, y: 1104, w: 126, h: 84 },
    { x: 0, y: 1188, w: 126, h: 84 },
    { x: 126, y: 1188, w: 126, h: 84 },
    { x: 2090, y: 340, w: 126, h: 68 },
    { x: 126, y: 1188, w: 126, h: 84 },
  ];

  if (canvasRef?.current) {
    let x = canvasRef?.current.getContext('2d');

    let img = new Image();
    img.src = path;

    //Draw Map Blocks
    //Here I deleted the extra codes, I just wanted to show that it was done this way.
    if (x) {
      x.drawImage(
        img,
        array[3].x,
        array[3].y,
        array[3].w,
        array[3].h,
        0,
        0,
        array[3].w,
        array[3].h
      );
    }
  }
}

这是我的结果:

在这里我需要您的指导来理解实现技巧。在这里,我们需要识别图像上的鼠标移动,或者我们需要一些旋转的正方形并具有图像并与 isPointInPath 函数一起使用。如果我们继续我提到的第二种方法,绘制正方形,我们需要rotate(-0.25 * Math.PI);

0


2 个回答
2

您需要跟踪光标位置,然后进行简单的基础更改,将屏幕坐标转换为游戏网格坐标:

  • 光标的位置v_C = (x, y)位于规范基础 中C

  • 你有一个替代基B(你的2.5D网格),它的替代基向量在basis中C是:

    columnVector_C = (cellHalfSizeLong, cellHalfSizeShort)
    rowsVector_C = (-cellHalfSizeLong, cellHalfSizeShort)
    
  • 您将它们放在矩阵的列中M_BC

    const M_BC = [
      [cellHalfSizeLong, -cellHalfSizeLong],
      [cellHalfSizeShort, cellHalfSizeShort],
    ];
    

    该矩阵可帮助您将向量从基础转换BC

  • 对该矩阵求逆即可得到M_CB。该矩阵可帮助您将向量从基向量转换CB

  • v_B = M_CB * v_C

  • 最后,确定坐标。这会告诉您在 2.5D 网格中选择/突出显示哪个单元格。

仍然:

  • 我不是数学家,所以我使用的命名法可能不正确,特别是考虑到我将更正式的向量/矩阵表达式与常规驼峰式变量名称混合在一起。

  • 看到它比阅读它更容易,所以,首先,观看这个:

这是一个工作示例:

// Some basic utils to work with matrices and vectors:

function matrixVectorMultiply(matrix, vector) {
  let result = [];

  for (let i = 0; i < matrix.length; i++) {
    let sum = 0;

    for (let j = 0; j < vector.length; j++) {
      sum += matrix[i][j] * vector[j];
    }

    result.push(sum);
  }

  return result;
}

function invertMatrix(matrix) {
  const n = matrix.length;

  let identity = [];

  for (let i = 0; i < n; i++) {
    identity.push([]);

    for (let j = 0; j < n; j++) {
      identity[i].push(i === j ? 1 : 0);
    }
  }

  // Apply Gauss-Jordan elimination:

  for (let i = 0; i < n; i++) {
    let pivot = matrix[i][i];

    for (let j = 0; j < n; j++) {
      matrix[i][j] /= pivot;
      identity[i][j] /= pivot;
    }

    for (let k = 0; k < n; k++) {
      if (k !== i) {
        let factor = matrix[k][i];

        for (let j = 0; j < n; j++) {
          matrix[k][j] -= factor * matrix[i][j];
          identity[k][j] -= factor * identity[i][j];
        }
      }
    }
  }

  return identity;
}

// Define the grid data (colors of each cell):

const gridData = [
  ['#008800', '#00FF00', '#008800', '#00FF00', '#008800', '#00FF00'],
  ['#00FF00', '#008800', '#00FF00', '#008800', '#00FF00', '#008800'],
  ['#008800', '#00FF00', '#000088', '#00FF00', '#008800', '#00FF00'],
  ['#00FF00', '#008800', '#00FF00', '#008800', '#00FF00', '#000088'],
  ['#008800', '#00FF00', '#008800', '#00FF00', '#000088', '#0000FF'],
  ['#00FF00', '#008800', '#00FF00', '#000088', '#0000FF', '#000088'],
];

// This is just for the demo. In a real application, the grid data matrix would
// probably contain all the information on each cell objects (array items):

const gridColorToType = {
  '#008800': 'Grass',
  '#00FF00': 'Grass',
  '#000088': 'Water',
  '#0000FF': 'Water',
};

const selectedCellBolor = '#000000';

// Get the UI elements:

const positionLabelElement = document.getElementById('positionLabel');
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

positionLabelElement.textContent = ' ';

// Adjust the canvas to the window:

const width = canvas.width = window.innerWidth;
const height = canvas.height = window.innerHeight;

// Grid sizing params:

const cellSizeLong = 100;
const cellHalfSizeLong = cellSizeLong / 2;
const cellSizeShort = cellSizeLong / 3;
const cellHalfSizeShort = cellSizeShort / 2;

// Keep track of the selected/highlighted cell:

let currentRow = 0;
let currentCol = 0;

// Drawing functions:

function drawCell(ctx, color, row, col) {
  ctx.fillStyle = color;

  // Calculate the position of the cell
  const x = (col - row) * cellHalfSizeLong + width / 2;
  const y = (col + row) * cellHalfSizeShort;

  // Fill:
  
  ctx.beginPath();
  ctx.moveTo(x, y);
  ctx.lineTo(x + cellHalfSizeLong, y + cellHalfSizeShort);
  ctx.lineTo(x, y + cellSizeShort);
  ctx.lineTo(x - cellHalfSizeLong, y + cellHalfSizeShort);
  ctx.closePath();
  ctx.fill();

  // Border:
  ctx.strokeStyle = '#000000';
  ctx.stroke();
}

function drawBoard() {
  ctx.clearRect(0, 0, width, height);
      
  const numRows = gridData.length;
  const numCols = gridData[0].length;

  // Draw all the cells in their respective color:
  
  for (let row = 0; row < numRows; ++row) {
    for (let col = 0; col < numCols; ++col) {      
      drawCell(ctx, gridData[row][col], row, col);
    }
  }
  
  // And re-draw the selected one on top (you might want to do this differently):
  drawCell(ctx, selectedCellBolor, currentRow, currentCol);
}

canvas.addEventListener('mousemove', () => {
    const x_C = width / 2 - event.clientX;
    const y_C = event.clientY;
    
    // First column is the columns vector in the 2.5D grid.
    // Second column is the rows vector in the 2.5 grid.
    const M_BC = [
      [cellHalfSizeLong, -cellHalfSizeLong],
      [cellHalfSizeShort, cellHalfSizeShort],
    ];
    
    // We need the inverse of that matrix to translate canonical basis
    // coordinates to coordinates in the 2.5D space's base:
    const M_CB = invertMatrix(M_BC);
    
    const [x_B, y_B] = matrixVectorMultiply(M_CB, [x_C, y_C]);
    const int_x_B = Math.floor(x_B);
    const int_y_B = Math.floor(y_B);
    
    currentRow = int_x_B;
    currentCol = int_y_B;
    
    const cellType = gridColorToType[gridData[currentRow]?.[currentCol]] || 'Void';
    
    positionLabelElement.textContent = `(${
      (x_C | 0).toFixed().padStart(4, ' ')
    }, ${
      (y_C | 0).toFixed().padStart(4, ' ')
    }) => (${
      x_B.toFixed(2).padStart(5, ' ')
    }, ${
      y_B.toFixed(2).padStart(5, ' ')
    }) => (${
      int_x_B.toFixed().padStart(2, ' ')
    }, ${
      int_y_B.toFixed().padStart(2, ' ')
    }) => ${ cellType }`;    
    
    requestAnimationFrame(() => {
      drawBoard();
    });
});

drawBoard();
body {
  background: #777;
}

#canvas {
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
}

#positionLabel {
  position: fixed;
  bottom: 0;
  left: 0;
  background: rgba(255, 255, 255, .5); 
  padding: 8px;
  border-radius: 0 4px 0 0;
  font-family: monospace;
  font-weight: bold;
  white-space: pre;
  backdrop-filter: blur(8px);
  pointer-events: none;
}
<canvas id="canvas"></canvas>

<div id="positionLabel"> <div>

如果您不关心像素完美的精度(例如,您不关心单元是否有一棵溢出一点的树,覆盖了后面的树,鼠标无法识别位于树的顶部),那么你就不需要了isPointInPath,而且你还获得了很多性能。

我建议研究

HTML 画布没有任何花哨的输入处理程序,它只是用于绘图。但是我们可以相对轻松地监听 mousemove 和 click 事件来与画布交互。

const canvasMap = document.getElementById("canvasMap");

// First method
canvasMap.onmousemove = function(event) {
    console.log(event);
}

// Second method
canvasMap.addEventListener("mousemove", function(event) {
    console.log(event);
    // If collision with tile, draw white border around it.
});

// For click events
canvasMap.addEventListener("click", function(event) {
    console.log(event);
});

然后,您可以获取clientXclientY属性并使用它们来计算鼠标与画布上的哪个图块相交。

如果画布不是位于左上角,我们可以获取边界客户端矩形,并从鼠标坐标中减去画布的clientLeftclientTop属性,以获得鼠标相对于画布的位置。

如果我们有相对于画布的坐标,那么它应该像碰撞检测一样简单,然后在画布中执行视觉更改。

对于碰撞检测,您可以参考并进行一些研究。

0