Изометрическая карта в шахматном порядке: вычисление координат карты для точки на экране

12

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

Вот изображение, которое показывает мою систему координат:

введите описание изображения здесь

Как преобразовать точку на экране в эту систему координат?

Крис
источник
Проверьте это: jsfiddle.net/Sd4ZP/18
Маркус фон Броади
Я не могу понять, как это полезно в любом случае. Я думаю, вы не совсем поняли, что я имею в виду.
Крис
Он выполняет преобразование, как раз наоборот, поэтому вам нужно обратить его вспять.
Маркус фон Броади

Ответы:

23

Во-первых, вот код. Объяснение будет следовать:

/*
 * tw, th contain the tile width and height.
 *
 * hitTest contains a single channel taken from a tile-shaped hit-test
 * image. Data was extracted with getImageData()
 */

worldToTilePos = function(x, y) {

    var eventilex = Math.floor(x%tw);
    var eventiley = Math.floor(y%th);

    if (hitTest[eventilex + eventiley * tw] !== 255) {
        /* On even tile */

        return {
            x: Math.floor((x + tw) / tw) - 1,
            y: 2 * (Math.floor((y + th) / th) - 1)
        };
    } else {
        /* On odd tile */

        return {
            x: Math.floor((x + tw / 2) / tw) - 1,
            y: 2 * (Math.floor((y + th / 2) / th)) - 1
        };
    }
};

Обратите внимание, что этот код не будет работать из коробки для карты, показанной в вашем вопросе. Это потому, что у вас нечетные плитки смещены влево, тогда как нечетные плитки чаще смещены вправо (как в случае с редактором мозаичных карт ). Вы можете легко исправить это, настроив значение x, возвращаемое в случае нечетного тайла.

объяснение

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

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

Две сетки, красная и зеленая

Обратите внимание, что справа от этого изображения я пометил точку с фиолетовой точкой. Это пример, который мы постараемся найти в нашем оригинальном тайле-пространстве.

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

Два региона-кандидата

Теперь, чтобы выбрать, какой из двух регионов является правильным. Всегда будет ровно один ответ.

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

Однако есть альтернативный способ. Для каждой тестовой области мы выбираем растровое изображение, которое соответствует точной форме наших плиток. Мы производим выборку в точке, переведенной в локальные координаты для этой отдельной плитки. Для нашего примера это будет выглядеть примерно так:

Точечные образцы

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

Затем мы приходим к выводу, что у нас есть хит в нечетном тайле на 1,1. Эта координата должна быть простой для сопоставления с исходными координатами плитки, используя другое преобразование для нечетных и четных строк.

Этот метод также позволяет вам иметь простые свойства для каждого пикселя в битовых картах теста пикселей. Например, белый не в цвете, черный - хит, синий - вода, красный - твердый.

IZB
источник
2
Это потрясающе!
HumanCatfood
Фантастический и хорошо объясненный ответ - теперь просто нужно внедрить его в код. Поскольку оригинальный постер не предоставил никакой информации о коде или языке, отличном от тега в Javascript, я говорю ответ A *
Tom 'Blue' Piddock
1

Я думаю, что у вас проблема с вашим пространством координат. Координаты, которые вы дали плиткам, на самом деле не являются изометрической проекцией - вам нужно думать о оси x как о диагонали, идущей вниз-вправо, а оси y как о диагонали, идущей вниз-влево (или некотором варианте этого)

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

Матрица преобразования, которую вы ищете, построена из этих осей x и y. Это практически то же самое, что и следующий расчет.

screenOffsetXY = screenPointXY - tileOriginXY;
tileX = dot(screenOffsetXY, xAxis) / tileWidth;
tileY = dot(screenOffsetXY, yAxis) / tileWidth;

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

Как преобразовать координаты мыши в изометрические индексы?

Cholesky
источник
0

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

function mouseTwoGridPosition(e){

var mousex = e.pageX; //mouse position x
var mouseY = e.pageY;  //mouse position y
var canvas_width = 1000; //pixels
var offset_left = 100; //offset of canvas to window in pixels
var offset_top = 150; //offset of canvas to window in pixels

var isotile = 64; //if iso tile is 64 by 64

//get mouse position relative to canvas rather than window
var x = mousex - canvas_width/2 - offset_left;
var y = mousey - offset_top;


//convert to isometric grid
var tx = Math.round( (x + y * 2) / isotile) - 1;
var ty = Math.round((y * 2 - x) / isotile) - 1;

 //because your grid starts at 0/0 not 1/1 we subtract 1 
 // this is optional based on what grid number you want to start at



   return [tx,ty];
}

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

Дейв
источник
Разве это не слишком просто? Я хочу, чтобы он идеально подходил к форме плитки iso. Вы просто проверяете прямоугольную область, если я правильно понимаю.
Крис
что вы подразумеваете под «формой» изотили… например, если ваша мышь находится в пределах ромбовидных границ 0: 1, возврат будет 0: 1, пока ваша мышь не выйдет за эту границу. Если ваши плитки различаются по размеру - тогда мой метод не сработает. Предоставленная функция - то, что я использую, и она прекрасно работает для меня.
Дэйв
Просто интересно, потому что все остальные решения намного сложнее. Плитки, конечно, одного размера, поэтому я это проверю.
Крис
Хорошо поработайте с ним, убедитесь, что вы получите правильное смещение холста влево и вверх - и ширину холста, когда вы сделаете математику, будет работать нормально (также ширина isotile size).
Дэйв
Вы уверены, что используете ту же систему координат, что и я? Это все еще полностью выключено.
Крис
0

Я решил это, изменив пространство координат. Теперь он начинается без смещения в первой строке, и для этого я нашел рабочий пример, который мне удалось немного отрегулировать.

    hWidth = this.tileset.tileSize.width / 2;
    hHeight = this.tileset.tileSize.height / 2;

    pX = point.x - halfWidth;
    pY = point.y - halfHeight;

    x = Math.floor((pX + (pY - hHeight) * 2) / this.tileset.tileSize.width);
    y = Math.floor((pY - (pX - hWidth) * 0.5) / this.tileset.tileSize.height);

    tx = Math.floor((x - y) / 2) + 1 + this.camera.x;
    ty = y + x + 2 + this.camera.y;
Крис
источник