Autotiling élégant


10

Je cherche des informations sur la façon dont les gens implémentent le tuilage automatique dans leurs jeux basés sur des tuiles. Jusqu'à présent, je l'ai toujours improvisé avec un tas de déclarations codées en dur "si ... sinon ...", et maintenant j'ai décidé qu'il était temps de trouver une solution plus élégante. Je suis allé chercher sur Internet des exemples d'implémentations et de discussions sur le sujet, mais je n'ai trouvé que trois articles:

(Surtout le dernier est complet et très utile.)

J'ai également examiné diverses implémentations et documentation de bibliothèques, qui l'implémentent, par exemple flixel: http://www.flixel.org/features.html#tilemaps

Malheureusement, toutes les solutions que j'ai pu trouver sont exactement aussi improvisées et aléatoires que ce que j'ai commencé et ne couvrent presque jamais tous les cas possibles.

Je cherche un exemple élégant d'implémentation de tuilage automatique dont je pourrais tirer des leçons.

Réponses:



3

Je suis arrivé ici en recherchant moi-même ce problème sur Google, en lisant les articles liés et en produisant une solution relativement compacte qui génère l'ensemble commun de 47 tuiles. Il nécessite un jeu de tuiles 2x3 pour le matériau autotiled comme ceci:un jeu de tuiles 2x3 autotile

Avec une variante à une seule tuile en haut à gauche, des coins intérieurs en haut à droite et quatre tuiles de coin extérieur en bas (vous pouvez reconnaître cet arrangement de RPG Maker).

L'astuce consiste à diviser chaque tuile de carte "logique" en 4 demi-tuiles pour le rendu. en outre, une demi-tuile dans le jeu de tuiles ne peut être dans cette position que dans une tuile générée, donc une demi-tuile en haut à gauche ne peut être utilisée que dans une position en haut à gauche.

Ces restrictions signifient que vous n'avez besoin de cocher que 3 voisins de tuiles complètes par demi-tuile, au lieu des 8 tuiles voisines.

J'ai rapidement mis en œuvre cette idée pour la tester. Voici le code de preuve de concept (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;
}    

Explication:

  • A est la partie supérieure gauche de la tuile, B est en haut à droite, Cest en bas à gauche, Dest en bas à droite.
  • edges contient des bitmasks pour chacun d'entre eux, nous pouvons donc saisir uniquement les informations de voisinage pertinentes.
  • map* sont des dictionnaires mappant les états voisins aux index graphiques dans l'image de jeu de tuiles (0..24).
    • puisque chaque demi-tuile vérifie 3 voisins, chacun a 2 ^ 3 = 8 états.
  • _tile est la tuile ciblée pour le tuilage automatique.
  • Puisque nos tuiles logiques sont deux fois plus grandes que nos tuiles de rendu, tous les cordons d'autotile (x, y) doivent être doublés dans la carte de rendu.

Quoi qu'il en soit, voici les résultats (avec une seule tuile, de toute façon):entrez la description de l'image ici


0

J'ai lu la plupart des liens et j'ai mis du temps à trouver une autre solution. Je ne sais pas si c'est bon ou pas, mais pour simuler le comportement de tuile automatique de RPG Maker VX Ace (47 tuiles), j'ai commencé à faire quelque chose comme ceci:

(gauche 0 ou 1) + (droite 0 ou 1) + (haut 0 ou 1) + (bas 0 ou 1) maintenant j'ai 5 cas.

si 4 = la tuile 46 est placée

si 3 pensionnaires =

si 2 4 cas + 2 cas pas sûr de l'algorithme mais pas beaucoup de branches à faire.

si 1 = travailler dessus mais chaque direction peut se retrouver dans 4 cas

si 0 = je peux utiliser l'algorithme numérique indiqué dans les liens avec 1, 2, 4, 8 et obtenir un identifiant de 1 à 15, je peux utiliser directement.

Je ne suis pas un programmeur et je ne suis pas le meilleur avec les algorithmes mathématiques et la solution 1, 2, 4, 8, 16, 32, 64, 128 que je n'aimais pas beaucoup non plus.

Peut-être que mon approche est au moins meilleure que ça.


1
Je ne suis pas sûr que cette réponse réponde pleinement à la question, pourriez-vous expliquer un peu plus? Si vous vous référez à autre chose, pourriez-vous au moins y faire un lien?
Vaillancourt
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.