Autotiling elegante

10

Estou procurando informações sobre como as pessoas implementam o autotiling em seus jogos baseados em blocos. Até agora, eu sempre o improvisei com várias frases "if ... else ..." codificadas e agora decidi que era hora de encontrar uma solução mais elegante. Pesquisei na Internet exemplos de implementações de autotiling e discussões sobre o tópico, mas criei apenas três artigos:

(Especialmente o último é abrangente e muito útil.)

Também observei várias implementações e documentação de bibliotecas que a implementam, por exemplo, flixel: http://www.flixel.org/features.html#tilemaps

Infelizmente, todas as soluções que eu pude encontrar são exatamente tão improvisadas e aleatórias quanto as que eu comecei, e quase nunca cobrem todos os casos possíveis.

Estou procurando um exemplo elegante de implementação de autotiling que eu possa aprender.

Radomir Dopieralski
fonte

Respostas:

10

Usei um método bit a bit para aplicar mapas de mosaico e achei uma solução muito elegante. O artigo fornece um exemplo concreto e discute como estender o algoritmo para lidar com vários tipos de terreno.

NoobsArePeople2
fonte
Parece o algoritmo usado pelo modo flixel "AUTO" de autotiling. Obrigado, estou adicionando à minha lista.
Radomir Dopieralski
3

Cheguei aqui pesquisando esse problema no Google, lendo os artigos vinculados e produzindo uma solução relativamente compacta que gera o conjunto comum de 47 peças. Requer um conjunto de peças 2x3 para o material autotiled como este:um conjunto de peças de autotile 2x3

Com uma variante de peça única no canto superior esquerdo, cantos internos no canto superior direito e quatro peças de canto externas na parte inferior (você pode reconhecer esse arranjo no RPG Maker).

O truque é dividir cada bloco de mapa "lógico" em quatro meios-blocos para renderização. além disso, um meio lado a lado no conjunto de peças só pode estar nessa posição em um lado esquerdo gerado, portanto, um lado lado esquerdo superior pode ser usado apenas na posição superior esquerda.

Essas restrições significam que você só precisa verificar três vizinhos de bloco inteiro por meio bloco, em vez dos oito blocos vizinhos.

Eu implementei essa ideia rapidamente para testá-la. Aqui está o código de prova de conceito (TypeScript):

//const dirs = { N: 1, E: 2, S: 4, W:8, NE: 16, SE: 32, SW: 64, NW: 128 };
const edges = { A: 1+8+128, B: 1+2+16, C: 4+8+64, D: 4+2+32 };
const mapA = { 0:8, 128:8, 1:16, 8:10, 9:2, 137:18, 136:10, 129:16 };
const mapB = { 0:11, 16:11, 1:19, 2:9, 3:3, 19:17, 18:9, 17:19 };
const mapC = { 0:20, 64:20, 4:12, 8:22, 12:6, 76:14, 72:22, 68:12 };
const mapD = { 0:23, 32:23, 4:15, 2:21, 6:7, 38:13, 34:21, 36:15 };

export function GenerateAutotileMap(_map: number[][], _tile: integer): number[][]
{
    var result = [];
    for (var y=0; y < _map.length; y++) {
        const row = _map[y];
        const Y = y*2;
        // half-tiles
        result[Y] = [];
        result[Y+1] = [];
        // each row
        for (var x=0; x < row.length; x++) {
            // get the tile
            const t = row[x];
            const X = x*2;
            if (t != _tile) continue;
            // Check nearby tile materials.
            const neighbors = (North(_map, x, y) == t? 1:0)
                + (East(_map, x, y) == t? 2:0)
                + (South(_map, x, y) == t? 4:0)
                + (West(_map, x, y) == t? 8:0)
                + (NorthEast(_map, x, y) == t? 16:0)
                + (SouthEast(_map, x, y) == t? 32:0)
                + (SouthWest(_map, x, y) == t? 64:0)
                + (NorthWest(_map, x, y) == t? 128:0);
            // Isolated tile
            if (neighbors == 0) {
                result[Y][X] = 0;
                result[Y][X+1] = 1;
                result[Y+1][X] = 4;
                result[Y+1][X+1] = 5;
                continue;
            }
            // Find half-tiles.
            result[Y][X] = mapA[neighbors & edges.A];
            result[Y][X+1] = mapB[neighbors & edges.B];
            result[Y+1][X] = mapC[neighbors & edges.C];
            result[Y+1][X+1] = mapD[neighbors & edges.D];
        }
    }
    return result;
}    

Explicação:

  • Aé a parte superior esquerda do bloco, Bé a parte superior direita, Cé a parte inferior esquerda, Dé a parte inferior direita.
  • edges mantém máscaras de bits para cada uma delas, para que possamos pegar apenas as informações relevantes dos vizinhos.
  • map* são dicionários que mapeiam estados vizinhos para índices gráficos na imagem do conjunto de peças (0..24).
    • Como cada meio mosaico verifica 3 vizinhos, cada um tem 2 ^ 3 = 8 estados.
  • _tile é o bloco destinado ao autotiling.
  • Como nossos blocos lógicos são duas vezes maiores que os blocos de renderização, todas as cordas de autotile (x, y) precisam ser duplicadas no mapa de renderização.

Enfim, aqui estão os resultados (com apenas um bloco, de qualquer maneira):insira a descrição da imagem aqui

Pennie Quinn
fonte
0

Eu li a maioria dos links e passei algum tempo para encontrar outra solução. Não sei se é bom ou não, mas para simular o comportamento de blocos automáticos do RPG Maker VX Ace (47 blocos), comecei a fazer algo assim:

(esquerda 0 ou 1) + (direita 0 ou 1) + (acima de 0 ou 1) + (abaixo de 0 ou 1) agora tenho 5 casos.

se 4 = Tile 46 é colocado

se 3 pensionistas =

se 2 4 casos + 2 casos não têm certeza sobre o algoritmo, mas não há muitos ramos a serem criados.

se 1 = trabalhando nele, mas todas as direções podem terminar em 4 casos

se 0 = eu posso usar o algoritmo numérico mostrado nos links com 1, 2, 4, 8 e obter id de 1 a 15 eu posso usar diretamente.

Não sou programador e não sou o melhor em algoritmos matemáticos e na solução 1, 2, 4, 8, 16, 32, 64, 128 também não gostei muito.

Talvez minha abordagem seja pelo menos melhor que isso.

Hajami
fonte
11
Não sei se essa resposta responde totalmente à pergunta. Você poderia explicar um pouco mais? Se você se referir a outra coisa, poderia ao menos criar um link para ela?
Vaillancourt