Generowanie mapy kafelków

25

Programuję grę opartą na kafelkach i mam kilka podstawowych kafelków (trawa, brud itp.), Ale nie mogę wymyślić, jak zrobić dobre losowe generowanie map, ponieważ kiedy dokonam naprawdę losowego wyboru, jeśli kafelek powinna być trawa / brud, dostaję to:

Obraz w grze

Rozumiem, dlaczego tak się dzieje, ale chcę stworzyć losowe ciągłe obszary trawy lub ziemi. Coś, co miałoby sens, na przykład:

Pożądany rezultat

Vilda
źródło
1
Oprócz proponowanych odpowiedzi możesz napisać własny silnik automatu komórkowego, aby wygenerować takie mapy. Modyfikując reguły automatu, można generować bardzo różne zachowania, prowadzące do bardzo różnych typów map. Na przykład, otrzymując automat 2d podobny do Życia, reguły powodziowe mogą prowadzić do „oceanu i wysp” (jak na zdjęciu powyżej), a reguła taka jak 1267/17 może prowadzić do pięknych labiryntów.
Manu343726,

Odpowiedzi:

19

Co możesz zrobić, to losowo wygenerować mapę Voronoi w następujący sposób:

  1. Wybieraj losowo center points(patrz czarne kropki) i losowo decyduj, czy to trawa, czy brud.
  2. Następnie w przypadku wszystkich płytek sprawdź, czy jest najbliżej center pointziemi lub brudu.
  3. Gotowy!

Jeśli to, co zrobiłeś wcześniej, to „przerzucenie monety” dla każdego kafelka (szum), wygenerowanie diagramu Voronoi zapewni znacznie lepszy wynik.

Możesz to poprawić, dzieląc center pointsna islandsalgorytm, który:

  1. Wybiera małą grupę centers pointsi określa je jako leaders.
  2. Iteracyjnie dodaje losowo sąsiadujący niezdecydowany punkt środkowy w każdej turze.
  3. Gotowy!

wprowadź opis zdjęcia tutaj

wolfdawn
źródło
1
Dzięki, ale wydaje się, że to bardzo trudne rozwiązanie.
Vilda
2
To nie jest bardzo trudne rozwiązanie. Najpierw losowo wybierz center points. Następnie zdecyduj, czy są to trawa, czy brud. Teraz przesuń pętlę nad tablicą i zdecyduj, czy każda płytka znajduje się najbliżej punktu brudu lub trawy. Może powiedz mi, która część stanowi wyzwanie?
wolfdawn
Mierzenie odległości. W każdym razie postaram się o tym powiadomić.
Vilda
Ok, więc udało mi się wygenerować kilka punktów. ( dropbox.com/s/dlx9uxz8kkid3kc/random_tile_map2.png ), więc moja cała klasa do generowania wygląda następująco: dropbox.com/s/pxhn902xqrhtli4/gen.java . Ale to wciąż nie działa.
Vilda
3
@ViliX twoje linki są zepsute
Artur Czajka
24

Można użyć szumu perlina, który jest zwykle używany do generowania mapy wysokości. Hałas Perlina w grach

Następnie możesz użyć wysokości jako doradcy, jak duża jest szansa na pojawienie się trawy / brudu w jednym regionie mapy.

Przykład (wartości hałasu Perlina od 0-256): Jeśli wartość przekracza 200, prawdopodobieństwo umieszczenia trawy wynosi 80% (brud 20%). Jeśli wartość wynosi od 100 do 200, prawdopodobieństwo umieszczenia trawy wynosi 50% (zabrudzenie również 50%). Jeśli wartość jest mniejsza niż 100, prawdopodobieństwo umieszczenia trawy wynosi 20% (brud 80%).

Klitz
źródło
Ok, powiedzmy, że mam tablicę [] [] liczby zmiennoprzecinkowej (0-1 prawdopodobieństwa) i mogę jej używać do odradzania płytek z prawdopodobieństwem. Ale jak wypełnić tę tablicę prawdopodobieństwa? Tablica ma (powiedzmy) 400 x 200, jak wypełnić ją wartościami prawdopodobieństwa?
Vilda
Sugeruje, aby wypełnić go hałasem perlina (zamiast białego szumu, takiego jak rzut monetą).
wolfdawn
Tak, ale jak mogę to osiągnąć?
Vilda
Link, który zamieściłem, może usunąć to pytanie. Najpierw wygeneruj szum, a następnie wygładź go. Rezultatem będzie dwuwymiarowa tablica, której możesz użyć do dalszej generacji mapy warstwowej. link
Klitz
To naprawdę dobra odpowiedź, ale rzadko osiąga wyniki podobne do tych, które przedstawiłeś.
wolfdawn
8

wprowadź opis zdjęcia tutaj

http://gamedevelopment.tutsplus.com/tutorials/generate-random-cave-levels-using-cellular-automata--gamedev-9664

oto moja wersja metody automatów komórkowych zacznij od losowego wypełnienia siatki, a następnie uruchom na niej te reguły automatów kulistych

  • Jeśli żywa komórka ma mniej niż dwóch żyjących sąsiadów, umiera.
  • Jeśli żywa komórka ma dwóch lub trzech żyjących sąsiadów, pozostaje przy życiu.
  • Jeśli żywa komórka ma więcej niż trzech żyjących sąsiadów, umiera.
  • Jeśli martwa komórka ma dokładnie trzech żywych sąsiadów, żyje.

i ostatecznie wygląda jak jaskinia

indeks można przekonwertować na pozycję x i y za pomocą tego kodu

public int TileIndex(int x, int y)
{
    return y * Generator.Instance.Width + x;
}
public Vector2 TilePosition(int index)
{
    float y = index / Generator.Instance.Width;
    float x = index - Generator.Instance.Width * y;
    return new Vector2(x, y);
}

po prostu zwracam listę booli, ponieważ używam tej listy do wielu rzeczy: jaskiń, drzew, kwiatów, trawy, mgły, wody możesz tutaj nawet łączyć wiele list na różne sposoby tutaj najpierw usuwam wszystkie mniejsze jaskinie, a następnie łączę dwie losowe listy

wprowadź opis zdjęcia tutaj

private int GetAdjacentCount(List<bool> list, Vector2 p)
{
    int count = 0;
    for (int y = -1; y <= 1; y++)
    {
        for (int x = -1; x <= 1; x++)
        {
            if (!((x == 0) && (y == 0)))
            {
                Vector2 point = new Vector2(p.x + x, p.y + y);
                if (PathFinder.Instance.InsideMap(point))
                {
                    int index = PathFinder.Instance.TileIndex(point);
                    if (list[index])
                    {
                        count++;
                    }
                }
                else
                {
                    count++;
                }
            }
        }
    }
    return count;
}
private List<bool> GetCellularList(int steps, float chance, int birth, int death)
{
    int count = _width * _height;
    List<bool> list = Enumerable.Repeat(false, count).ToList();
    for (int y = 0; y < _height; y++)
    {
        for (int x = 0; x < _width; x++)
        {
            Vector2 p = new Vector2(x, y);
            int index = PathFinder.Instance.TileIndex(p);
            list[index] = Utility.RandomPercent(chance);
        }
    }
    for (int i = 0; i < steps; i++)
    {
        var temp = Enumerable.Repeat(false, count).ToList();
        for (int y = 0; y < _height; y++)
        {
            for (int x = 0; x < _width; x++)
            {
                Vector2 p = new Vector2(x, y);
                int index = PathFinder.Instance.TileIndex(p);
                if (index == -1) Debug.Log(index);
                int adjacent = GetAdjacentCount(list, p);
                bool set = list[index];
                if (set)
                {
                    if (adjacent < death)
                        set = false;
                }
                else
                {
                    if (adjacent > birth)
                        set = true;
                }
                temp[index] = set;
            }
        }
        list = temp;
    }
    if ((steps > 0) && Utility.RandomBool())
        RemoveSmall(list);
    return list;
}
Rakka Rage
źródło
2
Wyjaśnij, co robi Twój kod, zamiast publikować próbkę tego, co robi, link i sam kod. Twoja odpowiedź powinna być samodzielna, aby nawet w przypadku zerwania linków nadal była użyteczna.
Pozew Fund Moniki w dniu
6

Wybierz punkt na mapie. Umieść żądany typ płytki o wartości bazowej, takiej jak 40. Śledź miejsce, w którym umieściłeś nowy pożądany kafelek. Dodaj punkt początkowy do listy.

Dla każdego punktu na tej liście odwiedzasz wszystkich sąsiadów. Gdy masz już dość mocy (od 40), dodaj żądany kafelek i dodaj go do listy, którą chcesz odwiedzić. Daj nowy kafelek mniej mocy, określonej przez ciebie. Najłatwiejszy = losowe opuszczanie. Po odwiedzeniu kafelka z listy usuń go. Zacznij od nowa, odwiedzając wszystkie nie odwiedzone, ale utworzone kafelki.

Jaapjan
źródło