Основная идея исходит из карты игры. Согласно моему обзору кода, карта представляет собой полностраничный холст. У меня нет проблем с рисованием изображений на холсте. Мой вопрос заключается в том, как обнаружить дома на карте и обновить холст или даже добавить к нему возможность щелчка. Я прикрепил код 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 [JS]](https://i.imgur.com/WsjO6zJb.png)


Предлагаю изучить события JavaScript.
HTML-холст не имеет каких-либо необычных обработчиков ввода, он предназначен только для рисования. Но мы можем относительно легко прослушивать события перемещения мыши и щелчка для взаимодействия с холстом.
const canvasMap = document.getElementById("canvasMap");
// First method
canvasMap.onmousemove = function(event) {
console.info(event);
}
// Second method
canvasMap.addEventListener("mousemove", function(event) {
console.info(event);
// If collision with tile, draw white border around it.
});
// For click events
canvasMap.addEventListener("click", function(event) {
console.info(event);
});
Затем вы можете взять свойства clientX и clientY и использовать их, чтобы вычислить, с каким тайлом на холсте пересекается мышь.
Если холст не расположен в левом верхнем углу, мы можем получить ограничивающий клиентский прямоугольник и вычесть свойства холста clientLeft и clientTop из координат мыши, чтобы получить положение мыши относительно холста.
Если у нас есть координаты относительно холста, это должно быть так же просто, как обнаружение столкновений с последующим визуальным изменением холста.
Для обнаружения столкновений вы можете обратиться к этому посту stackoverflow и провести небольшое исследование.
Вам нужно отслеживать положение курсора с помощью перемещения мыши, а затем сделать простое изменение основы, чтобы перевести координаты экрана в координаты игровой сетки:
Позиция курсора v_C = (x, y) находится в каноническом базисе, C.
У вас есть альтернативный базис B (ваша 2,5D-сетка) и его альтернативные базисные векторы в базисе C:
columnVector_C = (cellHalfSizeLong, cellHalfSizeShort)
rowsVector_C = (-cellHalfSizeLong, cellHalfSizeShort)
Вы помещаете их в столбцы матрицы M_BC:
const M_BC = [
[cellHalfSizeLong, -cellHalfSizeLong],
[cellHalfSizeShort, cellHalfSizeShort],
];
Эта матрица помогает вам переводить векторы из базиса B в C.
Инвертируйте эту матрицу, чтобы получить M_CB. Эта матрица помогает вам переводить векторы из базиса C в 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 = [
['#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, а еще вы получите большую производительность.