Elegancki autotiling


10

Szukam informacji o tym, jak ludzie implementują automatyczne zapisywanie w swoich grach opartych na kafelkach. Do tej pory zawsze improwizowałem za pomocą zestawu zakodowanych instrukcji „jeśli ... inaczej ...”, a teraz zdecydowałem, że nadszedł czas, aby znaleźć bardziej eleganckie rozwiązanie. Poszukałem w Internecie przykładów implementacji autotilingu i dyskusji na ten temat, ale wymyśliłem tylko trzy artykuły:

(Szczególnie ten ostatni jest obszerny i bardzo pomocny.)

Przyjrzałem się także różnym implementacjom i dokumentacji bibliotek, które ją implementują, na przykład flixel: http://www.flixel.org/features.html#tilemaps

Niestety, wszystkie rozwiązania, które mogłem znaleźć, są tak samo improwizowane i przypadkowe, jak to, co zacząłem i prawie nigdy nie obejmują wszystkich możliwych przypadków.

Szukam eleganckiego przykładu implementacji automatycznego zapisywania, z którego mógłbym się uczyć.

Odpowiedzi:



3

Przyszedłem tutaj, googling sam problem, przeczytałem powiązane artykuły i stworzyłem stosunkowo kompaktowe rozwiązanie, które generuje wspólny zestaw 47 płytek. Wymaga zestawu kafelków 2x3 dla autotilowanego materiału, takiego jak ten:zestaw klocków autotile 2x3

Z wariantem z pojedynczą płytką w lewym górnym rogu, wewnętrznymi narożnikami w prawym górnym rogu i czterema zewnętrznymi płytkami w rogu na dole (możesz rozpoznać ten układ w RPG Maker).

Sztuką jest podzielenie każdego „logicznego” kafelka mapy na 4 półpłytki do renderowania. ponadto półpłytka w zestawie klocków może znajdować się tylko w tej pozycji w wygenerowanym kafelku, więc lewy górny pół-kafelek może być użyty tylko w lewym górnym rogu.

Ograniczenia te oznaczają, że musisz sprawdzić tylko 3 sąsiadów z pełnymi kafelkami na pół kafelka, zamiast wszystkich 8 sąsiadujących kafelków.

Szybko wdrożyłem ten pomysł, aby go przetestować. Oto kod proof-of-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;
}    

Wyjaśnienie:

  • Ato górna lewa część kafelka, Bprawy górny róg, Clewy Ddolny róg, prawy dolny róg.
  • edges przechowuje maski bitów dla każdego z nich, dzięki czemu możemy pobrać tylko odpowiednie informacje o sąsiadach.
  • map* to słowniki odwzorowujące stany sąsiednie na wskaźniki graficzne w obrazie zestawu klocków (0..24).
    • ponieważ każdy półpłytek sprawdza 3 sąsiadów, każdy ma 2 ^ 3 = 8 stanów.
  • _tile to kafelek przeznaczony do autotilowania.
  • Ponieważ nasze logiczne kafelki są dwa razy większe niż nasze kafelki renderujące, wszystkie współrzędne autotile (x, y) muszą zostać podwojone na mapie renderowania.

Tak czy inaczej, oto wyniki (w każdym razie tylko z jednym kafelkiem):wprowadź opis zdjęcia tutaj


0

Przeczytałem większość linków i poświęciłem trochę czasu na wymyślenie innego rozwiązania. Nie wiem, czy to dobrze, czy nie, ale aby zasymulować zachowanie automatycznego kafelka RPG Maker VX Ace (47 kafelków), zacząłem robić coś takiego:

(w lewo 0 lub 1) + (w prawo 0 lub 1) + (w górę 0 lub 1) + (w dół 0 lub 1) teraz mam 5 przypadków.

jeśli 4 = płytka 46 jest umieszczona

jeśli 3 internatów =

jeśli 2 4 przypadki + 2 przypadki nie są pewne algorytmu, ale niewiele gałęzi do zrobienia.

jeśli 1 = pracuje nad tym, ale każdy kierunek może skończyć w 4 przypadkach

jeśli 0 = mogę użyć algorytmu liczbowego pokazanego w linkach z 1, 2, 4, 8 i uzyskać identyfikator od 1 do 15, którego mogę użyć bezpośrednio.

Nie jestem programistą i nie jestem najlepszy z algorytmami matematycznymi i rozwiązaniem 1, 2, 4, 8, 16, 32, 64, 128, które też mi się nie podobało.

Może moje podejście jest co najmniej lepsze.


1
Nie jestem pewien, czy ta odpowiedź w pełni odpowiada na pytanie, czy możesz wyjaśnić coś więcej? Jeśli odwołujesz się do czegoś innego, czy możesz przynajmniej link do tego?
Vaillancourt
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.