基本思想来自于游戏地图。根据我的代码审查,该地图是整页画布。我在画布上绘制图像没有问题。我的问题是如何检测地图房屋并更新画布,甚至为其添加点击功能。 我附上了原始游戏中的 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);
我建议查看 JavaScript 事件。
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);
});
然后您可以获取
clientX
和 clientY
属性并使用它们来计算鼠标与画布上的哪个图块相交。
如果画布不是位于左上角,我们可以获取边界客户端矩形,并从鼠标坐标中减去画布的
clientLeft
和 clientTop
属性,以获得鼠标相对于画布的位置。
如果我们有相对于画布的坐标,它应该像碰撞检测一样简单,然后在画布中执行视觉变化。
对于碰撞检测,您可以参考 this stackoverflow post 并做一些研究。
您只需要做一个简单的基础更改:
光标的
v_C = (x, y)
位于规范基础上,C
。
您有替代基础
B
(您的2.5D网格/投影)。
替代基向量是
columnVector_C = (cellHalfSizeLong, cellHalfSizeShort)
和 rowsVector_C = (-cellHalfSizeLong, cellHalfSizeShort)
您将它们放在矩阵的列中
M_BC
。该矩阵可帮助您将向量从基础 B
转换为规范基础。
将该矩阵求逆并得到
M_CB
。该矩阵可帮助您将向量从规范基础转换为基础 B
。
v_B = M_CB * v_C
。然后将坐标设置为底,这会告诉您在 2.5D 网格中选择/突出显示哪个网格。
仍然:
我不是数学家,所以我使用的命名法可能不正确,特别是考虑到我将更正式的向量/矩阵表达式与常规驼峰式变量名称混合在一起。
看到它比阅读它更容易,所以,首先,观看这个:3Blue1Brown - 基础的变化|第13章,线性代数的本质。
这是一个工作示例:
// 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 = [
['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#00FFFF', '#FF00FF'],
['#FFFF00', '#00FFFF', '#FF00FF', '#FF0000', '#00FF00', '#0000FF'],
['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#00FFFF', '#FF00FF'],
['#FFFF00', '#00FFFF', '#FF00FF', '#FF0000', '#00FF00', '#0000FF'],
['#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#00FFFF', '#FF00FF'],
['#FFFF00', '#00FFFF', '#FF00FF', '#FF0000', '#00FF00', '#0000FF'],
];
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;
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, ' ')
})`;
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);
}
<canvas id="canvas"></canvas>
<div id="positionLabel"> <div>