Круговое передвижение по карте

138
27 июля 2019, 16:30

Есть очень большой двумерный массив - игровая карта. Есть маленький двумерный массив - текущая, видимая часть игровой карты. Необходимо реализвать круговое (зацикленное) передвижение видимой части игровой карты. Чтобы при досттижении конца Глобальной карты, начинало отображаться начало Глобальной карты и так по кругу, в любую из сторон

Ниже, в собственном ответе, я показываю свою реализацию. Cделал как мог. Но хочется увидеть вариант от профи).

Answer 1

Вам надо переписать зубодробительный код в функции 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>

Answer 2

Выборка видимой части карты происходит в функции 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>

READ ALSO
Правильный счетчик вопросов

Правильный счетчик вопросов

Имеется несколько input'ов с одинаковым классом Как узнать какой инпут по счету сейчас активен?

131
Не работает enforceInMethodNames в eslint

Не работает enforceInMethodNames в eslint

Добавил в eslintrc такое правило: "rules": { "linebreak-style": 0, "enforceInMethodNames": true, "allowAfterThis": true }

116
Обход CORS (альтернатива yahooapis)

Обход CORS (альтернатива yahooapis)

Пользовался канувшей в лету yahooapis для обхода CORS, какие есть альтернативы без использования собственного сервера?

212
Замена повторяющегося выражения console.log(&#39;var = &#39;,var);

Замена повторяющегося выражения console.log('var = ',var);

Постоянно использую для отладки выражение вида

154