Эффективное получение изометрических позиций сетки внутри ограничительной рамки - PullRequest
17 голосов
/ 11 августа 2011

Isometric Grid

У меня есть система изометрической сетки, координаты которой начинаются с [0,0] в левом углу сетки (угол, показанный на изображении выше) с увеличением x в направлении нижней части изображения и увеличением y в направлении верхней части (поэтому [0, высота] будет верхним углом, а [ширина, 0] будет нижним углом в форме ромба, а ширина и высота будут размером сетки, т.е. 200 x 200 квадратов)

В любом случае мне нужна помощь в получении массива изометрических позиций сетки, которые содержатся в синей рамке, показанной на изображении. Если не считать итерации по каждому положению экрана x, y и получения соответствующей позиции сетки (см. Этот вопрос, который я задал ранее, о том, как преобразовать позицию экрана в позицию сетки Получить строку / столбец на изометрической сетке. ) я не уверен, как добиться этого эффективно.

Был вопрос, который я обнаружил ранее, который почти точно такой же Ссылка здесь. Ответ состоял в том, чтобы визуализировать сетку в изображение с разными цветами для каждого квадрата сетки, а затем определить, какие цвета присутствовали под квадрат, я реализовал это решение, но это довольно медленно! Я почти думаю, что проверка положения сетки для каждого пикселя в поле выбора будет быстрее. Почему javascript так медленно зацикливается!

Мне действительно нужно математическое решение этой проблемы, основанное на моей системе координат, но я не могу придумать что-то, что работает (и обрабатывает поле выбора, выходя за пределы сетки)

Пожалуйста, дайте мне знать, если вам нужна дополнительная информация.

Редактировать: К сожалению, предоставленные ответы до сих пор не сработали, так как выделение похоже на выделение области, выделенной ромбом, на квадратной сетке, в действительности нет верхнего левого и нижнего правого угла для итерации, если только я не пропустил точку ответы? Я оптимизировал метод рендеринга, но при большом выделении он по-прежнему добавляет заметное падение кадров, поскольку проходит через все пиксели, проверяя цвет и получая соответствующий квадрат

Ответы [ 6 ]

10 голосов
/ 19 августа 2011

Линейная алгебра является ответом.Здесь есть две системы координат: экранные координаты и изометрические координаты.Преобразование углов выбранной области из экранных координат в изометрические координаты очень поможет вам.

Пусть тета - это угол между осями x и y изометрических координат, измеренный на экране, а единица измерения - длина пикселя.одного шага в изометрических координатах.Тогда

var c = Math.cos(theta/2);
var s = Math.sin(theta/2);
var origin = [oX, oY]; //the pixel coordinates of (0, 0)
var unit = 20;
var iso2Screen = function(iso) {
  var screenX = origin[0] + unit * (iso[0] * c + iso[1] * c);
  var screenY = origin[1] + unit * (iso[0] * -s + iso[1] * s);
  return [screenX, screenY];
}

Инвертируя это соотношение, мы получим

var screen2Iso = function(screen) {
  var isoX = ((screen[0] - origin[0]) / c - (screen[1] - origin[1]) / s) / unit;
  var isoY = ((screen[0] - origin[0]) / c + (screen[1] - origin[1]) / s) / unit;

Теперь преобразуем экранные координаты каждого угла поля выбора в изометрические координаты и получаем минимальное и максимальное значения x и y.

var cornersScreen = ...//4-element array of 2-element arrays
var cornersIso = [];
for(var i = 0; i < 4; i++) {
  cornersIso.push(screen2Iso(cornersScreen[i]));
}
var minX, maxX, minY, maxY;
minX = Math.min(cornersIso[0][0], cornersIso[1][0], cornersIso[2][0], cornersIso[3][0]);
maxX = Math.max(cornersIso[0][0], cornersIso[1][0], cornersIso[2][0], cornersIso[3][0]);
minY = Math.min(cornersIso[0][1], cornersIso[1][1], cornersIso[2][1], cornersIso[3][1]);
maxY = Math.max(cornersIso[0][1], cornersIso[1][1], cornersIso[2][1], cornersIso[3][1]);

Все выбранные изометрические точки находятся внутри изометрического блока [minX, maxX] x [minY, maxY], но не все точки в этом поле находятся внутри выделения.

Вы могли бы сделать много разных вещей, чтобы отсеять точки в этом поле, которых нет в выделении;Я бы предложил перебирать целочисленные значения изометрических x и y, преобразовывать изометрические координаты в экранные координаты, а затем проверять, находится ли эта экранная координата в пределах поля выбора, до:

var sMinX, sMaxX, sMinY, sMaxY;
sMinX = Math.min(cornersScreen[0][0], cornersScreen[1][0], cornersScreen[2][0], cornersScreen[3][0]);
sMaxX = Math.max(cornersScreen[0][0], cornersScreen[1][0], cornersScreen[2][0], cornersScreen[3][0]);
sMinY = Math.min(cornersScreen[0][1], cornersScreen[1][1], cornersScreen[2][1], cornersScreen[3][1]);
sMaxY = Math.max(cornersScreen[0][1], cornersScreen[1][1], cornersScreen[2][1], cornersScreen[3][1]);
var selectedPoints = [];
for(var x = Math.floor(minX); x <= Math.ceil(maxX); x++) {
  for(var y = Math.floor(minY); x <= Math.ceil(maxY); y++) {
    var iso = [x,y];
    var screen = iso2Screen(iso);
    if(screen[0] >= sMinX && screen[0] <= sMaxX && screen[1] >= sMinY && screen[1] <= sMaxY) {
      selectedPoints.push(iso);
    }
  }
}
7 голосов
/ 11 августа 2011

Я учусь на XNA играх для хобби.Я работаю над классической 2D игрой на основе квадратов ( Project Squared ).Эта ваша игра напомнила мне о моей работе, поэтому я решил помочь вам.

Я думаю, что это должно быть сделано с использованием математики, а не графики, как вы упомянули.

Сначала вы должны знать, какой квадрат (позиция на сетке) находится в начале «синей рамки» (возможно, в поле выбора) и в конце выделения.Итак, теперь вы знаете начало и конец вашего списка.

Поскольку в вашей игре квадраты, вероятно, имеют статический размер (или вы увеличиваете камеру, но никогда не увеличиваете ширину / высоту), вы можете вычислить, какие квадраты находятся в поле выбора.легко.

Теперь вы знаете, что ваши квадраты сдвинуты на 45 ° об / л.

(я говорю о XNA, как координаты - вверх влево 0,0)

If ( selectedStartSquare.selectionY <= square.height )
    startY = selectedStartSquare.selectionY
else startY = selectedStartSquare.selectionY + 1; 

if (selectedEndSquare.selectionY > square.height)
    endY = -||-.selectionY
else endY = -||- + 1;

это даст вам начальный и конечный индексы высоты квадратов.То же самое, что вам нужно сделать для индексов X.

Теперь у вас есть все, что вам нужно, чтобы получить все квадраты.Пройдите через X от selectedStartSquare.X до endX и прокрутите Y с помощью цикла for с selectedStartSquare.Y до endY, но меняйте начало каждый раз (startYIndex ++ каждый цикл)

Это только близкий пример, так как я никогда не работал с изометрическими играми,Это, вероятно, потребует некоторой настройки, но я "думаю !!"это должно работать.(Написал это перед сном и даже без бумаги, так как я уже лежал в постели ... удачи)

Извините за мой английский, я из Хорватии, так что ...

4 голосов
/ 15 августа 2011

Принимая во внимание правильное расположение плиток, вы не можете просто начать с верхнего левого угла поля селектора, выполнить хит-тест, чтобы найти какую плитку, а затем перейти к следующей плитке в соответствии с их разнесением.*

Например, если ваши плитки 32х16, вы начинаете с угла и двигаетесь вдоль 32, а затем, достигнув конца, идете вдоль следующего ряда.

tile spacing

например, в странном псевдокоде ...

var altRow = false;
var selectedTiles = [];
for (int y = selectorBox.top; y < selectorBox.top+selectorBox.height; y+=TILE_HEIGHT/2) {
    for (int x = selectorBox.left ; x < selectorBox.left+selectorBox.width; x+= TILE_WIDTH) {
        selectedTiles.add(tileAtPoint(x + (altRow ? - TILE_WIDTH/2 : 0), y);
    }
    altRow = !altRow;
}
4 голосов
/ 11 августа 2011

Я думаю, что у Александра есть очень хорошая идея, как решить вашу проблему.

Вот альтернатива:

Простой способ уменьшить количество проверяемых квадратов сетки - найти координаты углов синего прямоугольника в координатах сетки. В вашем примере вам нужно будет только проверить квадраты, где 1<x<13 and 3<y<16.

Альваро дает короткий и краткий ответ на как проверить, находится ли точка внутри поля .

2 голосов
/ 17 августа 2011

Полагаю, вы можете получить координаты углов поля выбора, сопоставленных с квадратами изо-сетки.Это дает вам задачу построения графика с помощью простого алгебраического теста для решения.Если мы начнем с углов в изо-квадратах (x1, y1) и (x2, y2), мы знаем, что нам нужно протестировать вдоль линий с наклоном 1 и -1, которые проходят через эти точки,

, поэтомучетыре строки: y-y1 = x-x1, y-y1 = x1-x, y-y2 = x-x2, y-y2 = x2-x.Они встречаются в изо-квадратах, содержащих углы поля выбора, с которого вы начали.

Если для удобства предположить, что x2 и y2 больше, чем x1 и y1 соответственно, нам потребуется только итерация по изо-сеткедля значений от x1 до x2 и от y1 до y2, допускаются только изоквадраты, у которых координаты y меньше, чем обе «более крупные» линии, и меньше, чем две «меньшие» линии.

2 голосов
/ 16 августа 2011

Мой подход, которого я не видел ни у кого: 1. Преобразуйте выбранный прямоугольник в сетку (определите положение плитки для каждого из углов) 2. Теперь у нас есть двумерная задача: найти все точки в преобразованном ромбе.

Это можно сделать с помощью: http://en.wikipedia.org/wiki/Flood_fill

Но может быть, что-то вроде этого лучше:

1. Start at the topmost
2. Go down
3. Go right until we encounter the edge (can be calculated from the rombus line)
4. Do the same thing but go left.
5. Go to 2 until we are at the middle
6. Go right and down.
7. Go right until we encouter the edge
8. Go left and down.
9. Go left until we encouter the edge
10. Go to 6 until we are at the bottom

К каждой плитке, которую мы посещаем, мы добавляем эту.

Теперь единственной проблемой остается определить, встретились ли мы с ребром ...

...