Elegante autotiling


10

Sto cercando informazioni su come le persone implementano l'autotiling nei loro giochi basati su piastrelle. Finora l'ho sempre improvvisato con un mucchio di affermazioni "se ... altro ..." hardcoded, e ora ho deciso che è tempo di trovare una soluzione più elegante. Sono andato alla ricerca su Internet di esempi di implementazioni di auto-compilazione e discussioni sull'argomento, ma ho trovato solo tre articoli:

(Soprattutto l'ultimo è completo e molto utile.)

Ho anche esaminato varie implementazioni e documentazione delle librerie, che la implementano, ad esempio flixel: http://www.flixel.org/features.html#tilemaps

Purtroppo, tutte le soluzioni che sono riuscito a trovare sono esattamente improvvisate e casuali, come quello con cui ho iniziato, e non coprono quasi mai tutti i casi possibili.

Sto cercando un elegante esempio di implementazione di compilazione automatica da cui potrei imparare.

Risposte:



3

Sono arrivato qui cercando su Google questo problema, ho letto gli articoli collegati e prodotto una soluzione relativamente compatta che genera il set comune di 47 tessere. Richiede un set di tessere 2x3 per il materiale autotiledato in questo modo:un set di piastrelle autotile 2x3

Con una variante a tessera singola in alto a sinistra, angoli interni in alto a destra e quattro tessere angolari esterne in basso (puoi riconoscere questa disposizione da RPG Maker).

Il trucco è quello di spezzare ogni tessera "logica" in 4 semi-tessere per il rendering. inoltre, una mezza tessera nel set di tessere può trovarsi solo in quella posizione in una tessera generata, quindi una mezza tessera in alto a sinistra può essere utilizzata solo in una posizione in alto a sinistra.

Queste restrizioni indicano che è necessario controllare solo 3 vicini a tessera intera per mezza tessera, anziché tutte e 8 le tessere vicine.

Ho implementato rapidamente questa idea per testarla. Ecco il codice di prova (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;
}    

Spiegazione:

  • Aè la parte in alto a sinistra della tessera, Bè in alto a destra, Cè in basso a sinistra, Dè in basso a destra.
  • edges contiene maschere di bit per ognuna di queste, quindi possiamo prendere solo le informazioni relative al vicino.
  • map* sono dizionari che mappano gli stati vicini agli indici grafici nell'immagine tileset (0..24).
    • poiché ogni mezza tessera controlla 3 vicini, ognuno ha 2 ^ 3 = 8 stati.
  • _tile è la tessera scelta come target per l'autotiling.
  • Poiché le nostre tessere logiche sono due volte più grandi delle nostre tessere di rendering, tutti i coordini di autotile (x, y) devono essere raddoppiati nella mappa di rendering.

Ad ogni modo, ecco i risultati (con una sola tessera, comunque):inserisci qui la descrizione dell'immagine


0

Ho letto la maggior parte dei collegamenti e ho trascorso del tempo a trovare un'altra soluzione. Non so se va bene o no, ma per simulare il comportamento di tessera automatica RPG Maker VX Ace (47 tessere) ho iniziato a fare qualcosa del genere:

(sinistra 0 o 1) + (destra 0 o 1) + (su 0 o 1) + (giù 0 o 1) ora ho 5 casi.

se viene posizionato 4 = riquadro 46

se 3 pensionanti =

se 2 4 casi + 2 casi non sono sicuri dell'algoritmo ma non ci sono molti rami da creare.

se 1 = ci sta lavorando ma ogni direzione può finire in 4 casi

se 0 = posso usare l'algoritmo numerico mostrato nei collegamenti con 1, 2, 4, 8 e ottenere id da 1 a 15 posso usare direttamente.

Non sono un programmatore e non il migliore con gli algoritmi matematici e la soluzione 1, 2, 4, 8, 16, 32, 64, 128 non mi è piaciuta molto.

Forse il mio approccio è almeno migliore di così.


1
Non sono sicuro che questa risposta risponda pienamente alla domanda, potresti spiegarci un po 'di più? Se ti riferisci a qualcos'altro, potresti almeno collegarti ad esso?
Vaillancourt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.