Melhor técnica para desenhar ladrilhos isométricos


8

Estou pensando em criar um jogo isométrico simples com o HTML5 Canvas e me perguntando qual é a maneira mais rápida de renderizar os blocos.

Como os ladrilhos são em forma de diamante, mas o drawImage desenha retângulos, tenho que deixar de fora os cantos (as partes pretas a seguir):

amostra de ladrilhos de diamante

Eu acho que isso me deixa com três opções:

  1. Use objetos de imagem com canais alfa (.png). Receio que isso possa prejudicar o desempenho.
  2. Use um traçado de recorte. Se o renderizador for otimizado, isso pode ser bem rápido.
  3. Pré-renderização de blocos quadrados, assim:

ladrilhos quadrados pré-renderizados

Eu teria o quadrado preto como um ladrilho real na memória e desenharia esse ladrilho para o campo verde e todos os campos próximos a ele, ou acima e abaixo. Os campos diagonais (azuis) seriam constituídos pelos cantos dos ladrilhos retangulares. Isso evitaria os canais alfa ou de recorte, mas eu teria que pré-renderizar todas as combinações possíveis de blocos, e isso pareceria um exagero.

Qual é a melhor ou mais rápida maneira de fazer blocos isométricos? O que outros jogos, como FarmVille, usam?


11
Eu acho que a "melhor técnica" é meio carregada e vai depender dos requisitos do jogo, da tecnologia usada e do desenvolvedor que está criando o jogo.
Michaelhouse

11
Que problema você está realmente tendo? Parece que você forneceu três respostas para sua própria pergunta dentro do texto da pergunta?
perfil completo de Trevor Powell

11
Isso não tem votos suficientes: MELHOR MELHOR BESSSSSSST! argh?
Jimmy

11
Acho que você deve seguir os conselhos encontrados aqui: meta.gamedev.stackexchange.com/a/638/7191
MichaelHouse

11
Então você não sabe que isso é realmente um problema de desempenho? Você está apenas tentando otimizar o código antes que ele seja escrito? Nesse caso, -1 e votando para fechar.
Trevor Powell

Respostas:


11

Usar a transparência (canal alfa) é o caminho a seguir, eu recomendo.

Isso significa que quando você deseja um objeto vertical no bloco como este:

azulejo com árvore

Você poderá fazer isso facilmente se o seu renderizador desenhar os ladrilhos de trás para frente, ou seja, algoritmo de pintores.

CRÉDITO DE IMAGEM: tileset de Reiner.


9

Embora o método descrito por sws e MarkR também seja o que eu prefiro, gostaria de apresentar uma abordagem alternativa.

Uma opção imprudente para criar uma aparência isométrica com o mínimo esforço é realmente usar blocos ortogonais e context.transform para definir uma matriz de projeção que faz o mapa parecer isométrico (ou uma combinação de context.rotate e context.scale quando você não ' não sei como funcionam as matrizes de projeção).

Consulte a especificação para métodos de transformação de tela para obter detalhes.

Imagem lado a lado:

imagem do bloco base

Código de desenho:

    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 da aplicação da matriz:

sem matriz de transformação aplicada

O mesmo código com a mesma imagem de bloco após a aplicação desta matriz de transformação:

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

com matriz de transformação

Com a grade tracejada removida da imagem do bloco e alterando o deslocamento do bloco no código do desenho para img.width - 1e img.height - 1para eliminar as lacunas causadas pela transformação. De repente, o ladrilho parece meio feio:

com matriz e alguns ajustes

A principal desvantagem desse método é que, quando você cria seus blocos em um editor gráfico, eles não serão realmente o que você vê. Você também encontrará problemas quando quiser desenhar objetos que não estejam no chão, mas que estejam em pé. Para isso, você pode desativar a matriz de transformação antes de desenhá-la, mas precisará calcular a posição. Você pode usar estas fórmulas para isso:

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

(observe como os números da matriz de transformação reaparecem nessas fórmulas - você está fazendo a multiplicação de matrizes aqui).

Então, por que eu deveria fazer isso?

Este método é bom quando você:

  1. você não tem experiência em projetar blocos isométricos, mas você tem ortogonais
  2. não queira gastar muito tempo desenvolvendo um mecanismo gráfico isométrico, o que é um pouco mais difícil que o ortogonal.

Outra característica interessante é que, quando você conhece o cálculo da matriz, é possível modificar a matriz de projeção entre os quadros para aumentar o zoom, inclinar e girar o mapa em tempo real para obter alguns bons efeitos 3D falsos (tente fazer ISSO com ladrilhos isométricos) .

Mas quando você sabe como lidar com ladrilhos isométricos, tanto artística quanto tecnicamente, e não precisa de nenhum truque de perspectiva falsa, prefiro sugerir que você escolha ladrilhos em forma de diamante com transparência.


Certamente é possível fazer isso. Dessa forma, você pode ter blocos "planos" ou "de cima para baixo". No entanto, isso significa que qualquer elemento "3d" nos blocos não pode ser feito facilmente. Eu prefiro fazer essa transformação antes do tempo.
precisa saber é

+1 se não for apenas para a explicação especializada! Bem feito.
Luceos

@ MarkR: Ampliei muito minha resposta depois que você fez seu comentário, talvez você queira dar uma olhada se ela ainda se aplicar.
Philipp

11
Obrigado pelo artigo detalhado! Essa é uma técnica muito interessante, eu nunca pensei em fazer isso antes. No entanto, como estou começando do zero (e sou igualmente ruim em desenhar ladrilhos isométricos e quadrados ;-)), vou optar por ladrilhos isométricos verdadeiros. Além disso, isso parece não ser muito rápido. Fiz um pequeno benchmark e a combinação de blocos sem matriz de transformação para posições de pixels inteiros é cerca de 3x mais rápida do que com a transformação no meu PC. Ainda assim, posso usar isso para alguns efeitos especiais.
jdm

Alguma maneira de adicionar algum nível z a este mapa?
Tarion

3
  1. Você está usando context.drawImage para copiar dados de pixel de uma origem (imagem ou tela fora da tela) para outra (tela fora da tela ou na tela), que lida com o alfa normalmente fora da caixa. O Canvas é acelerado por hardware, portanto, o teste permitirá determinar se há uma diferença discernível nas velocidades de renderização em pixels alfa ou opacos.

  2. O recorte exigirá que você pressione / pop o estado do contexto ao definir um caminho de recorte uma vez por bloco, o que pode ser uma operação dispendiosa, considerando o excesso de isometria.

  3. Ladrilhos pré-renderizados exigiriam, como você declara, um número imenso de ladrilhos 'conectores' para serem desenhados, o que pode ou não ser viável. (Estou me inclinando mais para "talvez não").

Uma quarta solução seria adotar um "bloco" pré-renderizado (PRC) de blocos, gerado uma vez para telas fora da tela e depois cobrir a tela com os PRCs uma vez por quadro. Ainda haveria excesso, mas construir a RPC uma vez e renderizá-la com um deslocamento determinado pela posição do personagem do jogador (ou da câmera de visualização) em relação à RPC deve ser uma operação relativamente simples. Isso permitiria combinar a renderização com a opção 1, que é a melhor opção para IMO se o desempenho não fosse considerado (já que é mais simples de implementar).


3

Um pequeno canal alfa não dói muito, mas se você quiser evitá-lo, considere o uso de dois quartos, também oferece espaço para fazer boas transições de lado a lado sem fazer uma infinidade de imagens diferentes, essa é provavelmente a maior vantagem:

Padrão de sub-telha retangular

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.