Mapa isométrico escalonado: calcule las coordenadas del mapa para el punto en la pantalla

12

Sé que ya hay muchos recursos sobre esto, pero no he encontrado uno que coincida con mi sistema de coordenadas y estoy teniendo grandes problemas para ajustar cualquiera de esas soluciones a mis necesidades. Lo que aprendí es que la mejor manera de hacer esto es usar una matriz de transformación. Implementar eso no es un problema, pero no sé de qué manera tengo que transformar el espacio de coordenadas.

Aquí hay una imagen que muestra mi sistema de coordenadas:

ingrese la descripción de la imagen aquí

¿Cómo transformo un punto en la pantalla a este sistema de coordenadas?

Chris
fuente
Mira esto: jsfiddle.net/Sd4ZP/18
Markus von Broady
No puedo ver cómo esto es útil de ninguna manera. Creo que no entendiste lo que quiero decir.
Chris
Realiza la transformación, al revés, por lo que debe revertirla.
Markus von Broady

Respuestas:

23

Primero, aquí está el código. Seguirá una explicación:

/*
 * 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
        };
    }
};

Tenga en cuenta que este código no funcionará para el mapa que se muestra en su pregunta. Esto se debe a que los mosaicos impares se desplazan hacia la izquierda, mientras que el mosaico impar generalmente se desplaza hacia la derecha (como es el caso en el editor de mapas en mosaico ). Debería poder remediarlo fácilmente ajustando el valor x devuelto en el caso de mosaico impar.

Explicación

Esto puede parecer un método un poco más de fuerza bruta para lograr esta tarea, pero al menos tiene la ventaja de ser un píxel perfecto y un poco más flexible.

El truco consiste en ver el mapa no como una cuadrícula escalonada única, sino como dos cuadrículas superpuestas una encima de la otra. Existe la cuadrícula de filas impares y la cuadrícula de filas pares, pero en su lugar los llamamos rojo y verde para que podamos crear un diagrama bonito ...

Dos rejillas, rojas y verdes.

Observe a la derecha de esa imagen que he marcado un punto con un punto morado. Este es el punto de ejemplo que intentaremos encontrar en nuestro espacio de mosaico original.

Lo que debe notarse sobre cualquier punto del mundo es que siempre se ubicará exactamente en dos regiones: una roja y una verde (a menos que esté en un borde, pero de todos modos es probable que se recorte dentro del límite de borde irregular). Encontremos esas regiones ...

Dos regiones candidatas

Ahora para elegir cuál de las dos regiones es la correcta. Siempre habrá exactamente una respuesta.

Desde aquí podríamos hacer una aritmética más simple y calcular la distancia al cuadrado desde nuestro punto de muestra a cada punto central de las dos regiones. Cualquiera que sea el más cercano será nuestra respuesta.

Sin embargo, hay una forma alternativa. Para cada región de prueba, tomamos muestras de un mapa de bits que coincide con la forma exacta de nuestros mosaicos. Lo muestreamos en un punto traducido a coordenadas locales para ese mosaico único. Para nuestro ejemplo, se vería así:

Muestras puntuales

A la izquierda, verificamos la región verde y obtenemos un hit (píxel negro). A la derecha probamos la región roja y obtenemos una falla (píxel blanco). La segunda prueba es, por supuesto, redundante, ya que siempre será exactamente una u otra, nunca ambas.

Luego llegamos a la conclusión de que tenemos un golpe en el mosaico impar en 1,1. Esta coordenada debería ser simple de mapear a las coordenadas del mosaico original usando una transformación diferente para las filas pares e impares.

Este método también le permite tener propiedades simples por píxel en los mapas de bits de prueba de píxeles. Por ejemplo, el blanco es un mosaico, el negro es un éxito, el azul es agua, el rojo es sólido.

izb
fuente
2
¡Esto es asombroso³!
HumanCatfood 01 de
Respuesta fantástica y bien explicada: ahora solo es necesario implementarla en el código. Como el póster original no proporcionó ninguna información sobre el código detrás de él o el idioma que no sea la etiqueta en Javascript, digo una respuesta A *
Tom 'Blue' Piddock el
1

Creo que el problema que tienes es con tu espacio de coordenadas. Las coordenadas que le ha dado a los mosaicos no son realmente una proyección isométrica: debe pensar que el xAxis va en diagonal hacia abajo a la derecha y el yAxis en diagonal hacia abajo a la izquierda (o alguna variante de eso)

en este momento, si se mueve a lo largo de los ejes de coordenadas ilustrados, viajará en una dirección diagonal en "espacio de mosaico" para que los cuadrados terminen como diamantes (y todo será más complejo)

La matriz de transformación que está buscando está construida a partir de esos ejes x e y. Es efectivamente lo mismo que hacer el siguiente cálculo.

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

Editar: acabo de encontrar una pregunta similar (pero con el sistema de coordenadas del que estaba hablando) y alguien dio una respuesta mucho más completa aquí:

¿Cómo convertir las coordenadas del mouse en índices isométricos?

Cholesky
fuente
0

Básicamente, desea obtener la posición del mouse en la ventana utilizando un detector de eventos, debe eliminar el desplazamiento de la posición del lienzo en la ventana de las posiciones del mouse, de modo que la posición del mouse sea relativa al lienzo.

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];
}

Asumiré que sabe cómo escuchar eventos en el lienzo 'para mousemove, de lo contrario es posible que desee aprender más JS antes de considerar el diseño de juegos isométricos: D

Dave
fuente
¿No es esto demasiado simple? Quiero que coincida perfectamente con la forma del mosaico iso. Solo está comprobando un área rectangular si entiendo esto correctamente.
Chris
¿Qué quiere decir con "forma" del isotilo ... por ejemplo, digamos que su mouse está dentro de los límites en forma de diamante de 0: 1, el retorno será 0: 1 hasta que su mouse salga de ese límite. Si sus fichas varían en tamaño, entonces mi método no funcionará. La función proporcionada es lo que uso y funciona bien para mí.
Dave
Solo me pregunto, porque todas las otras soluciones son mucho más complicadas. Los azulejos son del mismo tamaño, por supuesto, así que lo comprobaré.
Chris
Juegue bien con él, asegúrese de obtener el desplazamiento correcto del lienzo hacia la izquierda y la parte superior, y el ancho del lienzo, cuando haga las matemáticas funcionará bien (también el ancho del tamaño de los isotiles).
Dave
¿Estás seguro de que estás usando el mismo sistema de coordenadas que yo? Todavía está completamente apagado.
Chris
0

Resolví esto cambiando el espacio de coordenadas. Ahora comienza sin un desplazamiento en la primera fila y para eso encontré un ejemplo de trabajo que pude ajustar un poco.

    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;
Chris
fuente