La mejor técnica para dibujar mosaicos isométricos.

8

Estoy pensando en hacer un juego isométrico simple con HTML5 Canvas y me pregunto cuál es la forma más rápida de renderizar los mosaicos.

Dado que los mosaicos tienen forma de diamante, pero drawImage dibuja rectángulos, tengo que dejar de lado las esquinas (las partes negras a continuación):

muestra de baldosas de diamantes

Creo que eso me deja con tres opciones:

  1. Use objetos de imagen con canales alfa (.png). Me temo que esto podría matar el rendimiento.
  2. Usa un trazado de recorte. Si el renderizador está optimizado, esto podría ser bastante rápido.
  3. Prerender azulejos cuadrados, como este:

azulejos cuadrados prerrendados

Tendría el cuadrado negro como un mosaico real en la memoria, y dibujaría dicho mosaico para el campo verde, y todos los campos al lado, o arriba y abajo. Los campos diagonales (azules) estarían formados por las esquinas de los mosaicos rectangulares. Esto evitaría el recorte o los canales alfa, pero tendría que representar previamente todas las combinaciones posibles de mosaicos, y parece excesivo.

¿Cuál es la mejor o más rápida forma de hacer mosaicos isométricos? ¿Qué usan otros juegos, como FarmVille?

jdm
fuente
1
Creo que la "mejor técnica" está un poco cargada y dependerá de los requisitos del juego, y de la tecnología utilizada y del desarrollador que cree el juego.
MichaelHouse
1
¿Qué problema tienes realmente? ¿Parece que ha proporcionado tres respuestas a su propia pregunta dentro del texto de la pregunta?
Trevor Powell
1
Esto no tiene suficientes votos: ¡ MEJOR MEJOR BESSSSSSST! argh?
Jimmy
1
Supongo que debe seguir los consejos que se encuentran aquí: meta.gamedev.stackexchange.com/a/638/7191
MichaelHouse
1
¿Entonces no sabes que esto es realmente un problema de rendimiento? ¿Solo estás tratando de optimizar el código antes de que esté escrito? En ese caso, -1 y votando para cerrar.
Trevor Powell

Respuestas:

11

Usar transparencia (canal alfa) es el camino a seguir, lo recomiendo.

Esto significa que cuando desea un objeto vertical en el mosaico como este:

azulejo con árbol

Entonces puede hacerlo fácilmente si su renderizador dibuja los mosaicos de atrás hacia adelante, es decir, el algoritmo de pintores.

CRÉDITO DE IMAGEN: juego de fichas de Reiner.

MarkR
fuente
9

Si bien el método descrito por sws y MarkR también es el que prefiero, me gustaría presentar un enfoque alternativo.

Una opción complicada para crear una apariencia isométrica con un esfuerzo mínimo es usar mosaicos ortogonales y usar context.transform para establecer una matriz de proyección que haga que el mapa se vea isométrico (o una combinación de context.rotate y context.scale cuando no lo hagas) No sé cómo funcionan las matrices de proyección).

Consulte la especificación de los métodos de transformación del lienzo para obtener más detalles.

Imagen de mosaico:

imagen de mosaico base

Código de dibujo:

    for (var x = 0; x < 5; x++) {
        for (var y = 0; y < 5; y++) {
            ctx.drawImage(img, x * img.width, y * img.height);
        }
    }

Resultado antes de la aplicación de la matriz:

sin matriz de transformación aplicada

Mismo código con la misma imagen de mosaico después de aplicar esta matriz de transformación:

    ctx.transform(  1,   0.5,
                   -1,   0.5,
                  160,   0    );

con matriz de transformación

Con la red de trazos eliminar de la imagen de azulejos y baldosas cambió el desplazamiento en el código de dibujo de img.width - 1y img.height - 1para deshacerse de los huecos causados por la transformación. De repente, el azulejo se ve la mitad de feo:

con matriz y algunos ajustes

El principal inconveniente de este método es que cuando diseñas tus mosaicos en un editor gráfico, realmente no serán lo que ves es lo que obtienes. También encontrará problemas cuando desee dibujar cualquier objeto que no esté en el piso pero que esté de pie. Para estos, puede desactivar la matriz de transformación antes de dibujarlos, pero luego tendrá que calcular la posición usted mismo. Puede usar estas fórmulas para eso:

var xScreen = xWorld * 1   + yWorld * -1  + 160;
var yScreen = xWorld * 0.5 + yWorld * 0.5 + 0;  

(observe cómo los números de la matriz de transformación reaparecen en estas fórmulas; usted mismo está haciendo la multiplicación de la matriz aquí).

Entonces, ¿por qué debería hacer esto?

Este método es bueno cuando usted:

  1. no tienes experiencia en el diseño de mosaicos isométricos, pero tienes otros ortogonales
  2. no quiero pasar mucho tiempo desarrollando un motor gráfico isométrico, que es algo más difícil que uno ortogonal.

Otra característica interesante es que cuando conoce el cálculo de la matriz, puede modificar la matriz de proyección entre cuadros para hacer zoom, inclinar y rotar el mapa en tiempo real para obtener algunos efectos fake-3d (intente hacer ESO con mosaicos isométricos) .

Pero cuando sabe cómo manejar mosaicos isométricos, tanto artística como técnicamente, y no necesita ningún truco de perspectiva falsa, prefiero sugerirle que elija mosaicos con forma de diamante con transparencia.

Philipp
fuente
Ciertamente es posible hacer esto. De esta manera puede tener mosaicos "planos" o "de arriba hacia abajo". Sin embargo, significa que cualquier elemento "3d" en los mosaicos no se puede hacer fácilmente. Prefiero hacer esta transformación antes de tiempo.
MarkR
¡+1 si no solo por la explicación experta! Bien hecho.
Luceos
@MarkR: amplié mi respuesta mucho después de que hiciste tu comentario, es posible que desees echar un vistazo si aún se aplica.
Philipp
1
¡Gracias por la descripción detallada! Esa es una técnica muy interesante, no pensé en hacer eso antes. Sin embargo, como estoy empezando desde cero (y soy igualmente malo dibujando mosaicos isométricos y cuadrados ;-)), buscaré verdaderos mosaicos isométricos. Además, esto parece no ser muy rápido. Hice un pequeño punto de referencia, y eliminar mosaicos sin una matriz de transformación a posiciones de píxeles enteras es aproximadamente 3 veces más rápido que con la transformación en mi PC. Aún así, podría usar esto para algunos efectos especiales.
jdm
¿Alguna forma de agregar un nivel z a este mapa?
Tarion
3
  1. Está utilizando context.drawImage para copiar datos de píxeles de una fuente (imagen o lienzo fuera de pantalla) a otra (fuera de la pantalla o lienzo en pantalla), que maneja alfa de manera elegante desde el primer momento. El lienzo está acelerado por hardware, por lo que las pruebas le permitirán determinar si hay una diferencia apreciable en las velocidades de renderizado wrt alfa vs píxeles opacos.

  2. El recorte requerirá empujar / reventar el estado del contexto al definir una ruta de recorte una vez por mosaico, lo que podría ser una operación costosa considerando el sobregiro isométrico.

  3. Como usted dice, los mosaicos prerrepresentados requerirían una inmensa cantidad de mosaicos 'conectores' para ser dibujados, lo que puede o no ser factible. (Me estoy inclinando más hacia "quizás no").

Una cuarta solución sería adoptar un "trozo" preprocesado (PRC) de mosaicos, generado una vez fuera de los lienzos de pantalla, y luego cubrir la pantalla con los PRC una vez por cuadro. Todavía habría sobregiro, pero construir el PRC una vez y representarlo con un desplazamiento determinado por la posición del personaje del jugador (o ver la cámara) en relación con el PRC debería ser una operación relativamente simple. Esto le permitiría combinar el renderizado con la opción n. ° 1, que es la mejor opción de IMO si no se tuviera en cuenta el rendimiento (ya que es más sencillo de implementar).

sws
fuente
3

Un pequeño canal alfa no duele mucho, pero si desea evitarlo, considere usar dos cuartos de mosaico, esto también le da espacio para realizar agradables transiciones de mosaico sin hacer una gran cantidad de imágenes diferentes, esa es probablemente la mayor ventaja:

Patrón rectangular de sub-mosaico

aaaaaaaaaaaa
fuente