Есть очень большой двумерный массив - игровая карта. Есть маленький двумерный массив - текущая, видимая часть игровой карты. Необходимо реализвать круговое (зацикленное) передвижение видимой части игровой карты. Чтобы при досттижении конца Глобальной карты, начинало отображаться начало Глобальной карты и так по кругу, в любую из сторон
Ниже, в собственном ответе, я показываю свою реализацию. Cделал как мог. Но хочется увидеть вариант от профи).
Вам надо переписать зубодробительный код в функции getGlobalMapSector
. Если я правильно понял, она выбирает из большого двумерного массива двумерный кусок с заворачиванием через границы большого массива. Все, что для этого нужно, - текущее положение левого-верхнего угла меньшего массива в большем и обращение к элементам большего массива с использованием остатка от деления.
Вот одномерный случай:
var sizeLarge = 10;
var sizeSmall = 4;
var start = 0;
var large = [];
for (var i = 0; i < sizeLarge; i++) {
large.push(i + 1);
}
function getSmall() {
if (start < 0)
start += sizeLarge;
var small = [];
for (var j = 0; j < sizeSmall; j++) {
small.push(large[(start + j) % sizeLarge]);
}
return small;
}
function showSmall() {
console.log(JSON.stringify(getSmall()));
}
showSmall();
<button onclick="start--;showSmall()">Left</button>
<button onclick="start++;showSmall()">Right</button>
Выборка видимой части карты происходит в функции getGlobalMapSector
. Все остальное для визуализации
const GlobalMap = [];
const numSection = 30; //длина GlobalMap
const viewNumSection = 11; //длина видимой части карты
//центр видимой части карты
const centerMap = {
x: 10,
y: 10,
};
//видимая часть карта
let currentMap = [];
const isoCoords = {
x: 0,
y: 200,
};
const tileWidth = 36;
const canvas = document.getElementById("canvas");
const viewX = document.getElementById('rombX')
const viewY = document.getElementById('rombY')
const ctx = canvas.getContext("2d");
canvas.addEventListener("mousemove", mouseMoveOnMap);
const colors = {
0: "green",
1: "blue",
2: "black",
3: "yellow",
};
for (let i = 0; i < numSection; i++) {
const row = [];
GlobalMap.push(row);
for (let h = 0; h < numSection; h++) {
const obj = {
x: i,
y: h,
type: Math.floor(Math.random() * 4),
};
row.push(obj);
}
}
currentMap = getGlobalMapSector();
drawMap();
function getGlobalMapSector() {
let rangeArr = [];
let length = GlobalMap.length;
let rangeSize = viewNumSection;
rangeSize = rangeSize % 2 !== 0 ? rangeSize : rangeSize + 1; //костыль
let halfSize = Math.floor(rangeSize / 2);
let minX = centerMap.x - halfSize;
let minY = centerMap.y - halfSize;
let startX = minX;
let startY = minY;
let endX = minX + rangeSize;
let endY = minY + rangeSize;
//передвижение внутри карты
if (
minX >= 0 &&
minY >= 0 &&
minX + rangeSize <= length &&
minY + rangeSize <= length
) {
// console.log("передвижение внутри карты")
let endX = minX + rangeSize;
let endY = minY + rangeSize;
rangeArr = getSectorMapList(minX, endX, minY, endY, rangeArr);
rangeArr = formResultMap(rangeArr, rangeSize);
}
//верхний левый угол карты
else if (minX < 0 && minY < 0) {
let startX = length - Math.abs(minX);
let endX = length;
let startY = length - Math.abs(minY);
let endY = length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startY = 0;
endY = 0 + (rangeSize - (length - (length - Math.abs(minY))));
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startX = 0;
endX = 0 + (rangeSize - (length - (length - Math.abs(minX))));
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startY = length - Math.abs(minY);
endY = length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
sortArr_standart(rangeArr);
changeArr(rangeArr, length - Math.abs(minX), "x");
rangeArr = formResultMap(rangeArr, rangeSize);
rangeArr = sortArr_Y(rangeArr, length - Math.abs(minY));
}
//нижний правый угол карты
else if (minX + rangeSize >= length && minY + rangeSize >= length) {
let startX = minX;
let endX = length;
let startY = minY;
let endY = length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startX = 0;
endX = minX + rangeSize - length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startY = 0;
endY = minY + rangeSize - length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startX = minX;
endX = length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
sortArr_standart(rangeArr);
changeArr(rangeArr, minX, "x");
rangeArr = formResultMap(rangeArr, rangeSize);
rangeArr = sortArr_Y(rangeArr, minY);
}
//верхний правый угол карты
else if (minX < 0 && minY + rangeSize > length) {
let startX = length - Math.abs(minX);
let endX = length;
let startY = minY;
let endY = length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startX = 0;
endX = 0 + (rangeSize - (length - (length - Math.abs(minX))));
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startY = 0;
endY = minY + rangeSize - length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startX = length - Math.abs(minX);
endX = length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
sortArr_standart(rangeArr);
changeArr(rangeArr, length - Math.abs(minX), "x");
rangeArr = formResultMap(rangeArr, rangeSize);
rangeArr = sortArr_Y(rangeArr, minY);
}
//нижний левый угол карты
else if (minY < 0 && minX + rangeSize > length) {
let startX = minX;
let endX = length;
let startY = length - Math.abs(minY);
let endY = length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startY = 0;
endY = 0 + (rangeSize - (length - (length - Math.abs(minY))));
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startX = 0;
endX = minX + rangeSize - length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startY = length - Math.abs(minY);
endY = length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
sortArr_standart(rangeArr);
changeArr(rangeArr, length - Math.abs(minX), "x");
rangeArr = formResultMap(rangeArr, rangeSize);
rangeArr = sortArr_Y(rangeArr, minX);
}
//по центру верхней линии карты
else if (minX < 0 && minY >= 0 && minY + rangeSize <= length) {
let startX = length - Math.abs(minX);
let endX = length;
let startY = minY;
let endY = minY + rangeSize;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startX = 0;
endX = 0 + (rangeSize - (length - (length - Math.abs(minX))));
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
sortArr_standart(rangeArr);
changeArr(rangeArr, length - Math.abs(minX), "x");
rangeArr = formResultMap(rangeArr, rangeSize);
rangeArr = sortArr_Y(rangeArr, minX);
}
//по центру нижней линии карты
else if (
minY >= 0 &&
minY + rangeSize < length &&
minX >= 0 &&
minX + rangeSize > length
) {
let startX = minX;
let endX = length;
let startY = minY;
let endY = minY + rangeSize;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startX = 0;
endX = minX + rangeSize - length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
sortArr_standart(rangeArr);
changeArr(rangeArr, minX, "x");
rangeArr = formResultMap(rangeArr, rangeSize);
}
//по центру правой линии
else if (
minX >= 0 &&
minX + rangeSize < length &&
minY >= 0 &&
minY + rangeSize > length
) {
let startX = minX;
let endX = minX + rangeSize;
let startY = minY;
let endY = length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startY = 0;
endY = minY + rangeSize - length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
sortArr_standart(rangeArr);
rangeArr = formResultMap(rangeArr, rangeSize);
rangeArr = sortArr_Y(rangeArr, minY);
}
//по центру левой линии
else if (minY < 0 && minX >= 0 && minX + rangeSize <= length) {
startX = minX;
endX = minX + rangeSize;
startY = length - Math.abs(minY);
endY = length;
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
startY = 0;
endY = 0 + (rangeSize - (length - (length - Math.abs(minY))));
rangeArr = getSectorMapList(startX, endX, startY, endY, rangeArr);
sortArr_standart(rangeArr);
rangeArr = formResultMap(rangeArr, rangeSize);
rangeArr = sortArr_Y(rangeArr, length - Math.abs(minY));
}
let center = rangeArr.length - 1 - halfSize;
let centerSection = rangeArr[center][center];
centerMap.x = centerSection.x;
centerMap.y = centerSection.y;
return rangeArr;
//сортирую элементы по Х и по У
function sortArr_standart(arr) {
arr.sort((a, b) => {
let z = a.x - b.x;
if (z == 0) {
return a.y - b.y;
} else {
return z;
}
});
}
//сортирую массив по У
function sortArr_Y(arr, num) {
for (let i = 0; i < arr.length; i++) {
let g = arr[i];
changeArr(g, num, "y");
}
return arr;
}
//перекидываю с конца массива в начало необходимые елементы
//num - число, начиная с которого нужно переместить елементы
function changeArr(arr, num, os) {
let g = [];
for (let i = 0; i < arr.length; i++) {
let sector = arr[i];
if (sector[os] >= num) {
g = arr.splice(i, arr.length);
break;
}
}
for (let h = g.length - 1; h >= 0; h--) {
arr.unshift(g[h]);
}
}
//делаю выборку из исходного двумерного массива
function getSectorMapList(startX, endX, startY, endY, arr) {
for (let x = startX; x < endX; x++) {
for (let y = startY; y < endY; y++) {
arr.push(GlobalMap[x][y]);
}
}
return arr;
}
//формирую двумерный массив из исходного списка
function formResultMap(arr, size) {
let count = 0;
let itogArr = [];
for (let i = 0; i < size; i++) {
let f = [];
itogArr.push(f);
for (let h = 0; h < size; h++) {
itogArr[i][h] = arr[count];
count++;
}
}
return itogArr;
}
}
// меняет значение centerMap, currentMap
function moveOnMap(way) {
let step = 2;
let nowX = centerMap.x;
let nowY = centerMap.y;
if (way == "top") {
centerMap.x = nowX + step;
if (nowX + step > GlobalMap.length - 1) {
centerMap.x = nowX + step - GlobalMap.length;
}
} else if (way === "bottom") {
centerMap.x = nowX - step;
if (nowX - step < 0) {
centerMap.x = GlobalMap.length - Math.abs(nowX - step);
}
} else if (way === "left") {
centerMap.y = nowY - step;
if (nowY - step < 0) {
centerMap.y = GlobalMap.length - Math.abs(nowY - step);
}
} else if (way === "right") {
centerMap.y = nowY + step;
if (nowY + step > GlobalMap.length - 1) {
centerMap.y = nowY + step - GlobalMap.length;
}
}
currentMap = getGlobalMapSector();
drawMap();
}
function drawMap() {
let mapArr = currentMap;
ctx.clearRect(0, 0, 400, 400);
let tileHeight = tileWidth / 2;
let halfHeight = tileHeight / 2;
let startX = isoCoords.x;
let startY = isoCoords.y;
let startCenterX = startX + tileHeight;
let startCenterY = startY;
for (let i = 0; i < mapArr.length; i++) {
for (let h = 0; h < mapArr[i].length; h++) {
let centerX = startCenterX + 2 * halfHeight * (i + h);
let centerY = startCenterY - halfHeight * (i - h);
currentMap[i][h].centerX = centerX;
currentMap[i][h].centerY = centerY;
drawRectAroundCenter(centerX, centerY, mapArr[i][h].type);
}
}
function drawRectAroundCenter(centerX, centerY, grid) {
const step = 0;
ctx.beginPath();
ctx.fillStyle = colors[grid];
ctx.strokeStyle = "black";
ctx.moveTo(centerX, centerY - halfHeight + step);
ctx.lineTo(centerX + step - tileHeight, centerY);
ctx.lineTo(centerX, centerY + halfHeight - step);
ctx.lineTo(centerX + tileHeight - step, centerY);
ctx.lineTo(centerX, centerY - halfHeight + step);
ctx.stroke();
ctx.fill();
ctx.closePath();
}
}
const mouseCoords = {
x: 0,
y: 0,
};
function getCursorPositionOnScene(event) {
const clientX = event.clientX;
const clientY = event.clientY;
const position = canvas.getBoundingClientRect();
const mouseX = Math.floor(clientX - position.left);
const mouseY = Math.floor(clientY - position.top);
mouseCoords.x = mouseX;
mouseCoords.y = mouseY;
return;
}
function getTileCoordsOnMap() {
const halfHeight = tileWidth / 2 / 2;
const isoX = isoCoords.x;
const isoY = isoCoords.y;
const stepX = mouseCoords.x - isoX;
const stepY = mouseCoords.y - isoY;
const topX = 0.5 * stepX - stepY + isoX;
const topY = 0.5 * stepY - stepX / 4 + isoY;
const downX = 0.5 * stepX + stepY + isoX;
const downY = 0.25 * stepX + 0.5 * stepY + isoY;
let q = Math.pow(topX - isoX, 2) + Math.pow(topY - isoY, 2);
const l = halfHeight * Math.sqrt(5);
const lineTop = Math.sqrt(q);
const rombX = Math.floor(lineTop / l);
q = Math.pow(downX - isoX, 2) + Math.pow(downY - isoY, 2);
const lineDown = Math.sqrt(q);
const rombY = Math.floor(lineDown / l);
return (coords = {
x: rombX,
y: rombY,
});
}
function mouseMoveOnMap(event) {
getCursorPositionOnScene(event);
if (!checkMouseCoordsOnMap()) return;
const rombCoords = getTileCoordsOnMap();
const tile = currentMap[rombCoords.x][rombCoords.y]
viewX.innerText = "X:" + tile.x;
viewY.innerText = "Y:" + tile.y;
}
const borderIsoMap = {
left: {
x: 0,
y: 0
},
top: {
x: 0,
y: 0
},
right: {
x: 0,
y: 0
},
bottom: {
x: 0,
y: 0
},
};
setBorderIsoMap();
function setBorderIsoMap() {
const currentLength = currentMap.length;
const height = tileWidth / 2;
borderIsoMap.left.x = isoCoords.x;
borderIsoMap.left.y = isoCoords.y;
borderIsoMap.top.x = borderIsoMap.left.x + (tileWidth * currentLength) / 2;
borderIsoMap.top.y = borderIsoMap.left.y - (height * currentLength) / 2;
borderIsoMap.right.x = borderIsoMap.left.x + tileWidth * currentLength;
borderIsoMap.right.y = borderIsoMap.left.y;
borderIsoMap.bottom.x = borderIsoMap.top.x;
borderIsoMap.bottom.y = borderIsoMap.left.y + (height * currentLength) / 2;
}
function checkMouseCoordsOnMap() {
const coords = borderIsoMap;
const left = coords.left;
const top = coords.top;
const right = coords.right;
const bottom = coords.bottom;
ctx.beginPath();
ctx.strokeStyle = "transparent";
ctx.moveTo(left.x, left.y);
ctx.lineTo(top.x, top.y);
ctx.lineTo(right.x, right.y);
ctx.lineTo(bottom.x, bottom.y);
ctx.stroke();
ctx.closePath();
return ctx.isPointInPath(mouseCoords.x, mouseCoords.y);
}
#scene {
width: 400px;
height: 400px;
border: 1px solid;
position: relative;
}
button {
position: absolute;
}
.bottom {
bottom: 70px;
left: 80px;
}
.left {
top: 80px;
left: 90px;
}
.top {
right: 80px;
top: 80px;
}
.right {
bottom: 70px;
right: 60px;
}
#rombX {
position: absolute;
top: 10px;
left: 10px;
}
#rombY {
position: absolute;
top: 30px;
left: 10px;
}
<div id="scene">
<canvas id='canvas' width="400px" height="400px"></canvas>
<button class="left" type="button" onclick="moveOnMap('left')">left</button>
<button class="top" type="button" onclick="moveOnMap('top')">top</button>
<button class="right" type="button" onclick="moveOnMap('right')">right</button>
<button class="bottom" type="button" onclick="moveOnMap('bottom')">bottom</button>
<div id="rombX">X: -1</div>
<div id="rombY">Y: -1</div>
</div>
Виртуальный выделенный сервер (VDS) становится отличным выбором
Имеется несколько input'ов с одинаковым классом Как узнать какой инпут по счету сейчас активен?
Добавил в eslintrc такое правило: "rules": { "linebreak-style": 0, "enforceInMethodNames": true, "allowAfterThis": true }
Пользовался канувшей в лету yahooapis для обхода CORS, какие есть альтернативы без использования собственного сервера?
Постоянно использую для отладки выражение вида