W dzieciństwie na dyskotekach szkolnych zawsze byłem w grupie, która ściany podpierała zawodowo, ale to nie o tych ścianach mowa. W dzisiejszym odcinku uzupełnię ten pustostan w stanie developerskim o jakiekolwiek ściany. Wiecie prywatność dla wrogów, tajemnica, drzwi i takie tam! Zapraszam.

Założenie generatora

Najpierw męczyłem się nad algorytmem losowania rozmiaru pomieszczeń, ale potem zostałem oświecony patelnią, że rozmiar to nie wszystko, trzeba te ściany jeszcze ułożyć na mapie, bo inaczej byłaby ostra mieszanka pomieszczeń, która mogłaby wyjść nawet i poza ramy array’a 🙂 Niestety, zanim zacząłem generować, tak jak poniżej, wpadłem na idiotyczny sposób zrobienia pokoi w pętli, która zawierała pętle … w której kolejne pętle generowały ścianę pionową i poziomą dla każdego pokoju 🙂
W skrócie: W czasie studiów w języku PHP (no more!!), stworzyłem skrypt, który generował parę ciągów matematycznych i drukował je na ekranie. Po paru minutach potrafił zawiesić dowolną przeglądarkę. Tutaj poszedłem o krok dalej! Najpierw zablokował się Unity, myślałem, że mieli trochę dłużej, ale kiedy rano otworzyłem Maca, okazało się, że przeciążyło pamięć fizyczną, którą zrzuciło na pamięć wirtualną … i zapchało resztę dysku 🙂 Brawo ja! Wolno zapychający wirus … jestem lepszy niż Kaspersky!

Linie poziome!

Takim oto sposobem przyjąłem inny scenariusz.
Z racji, że pokoje muszą mieścić się w określonym limicie, zacząłem od przypisania wartości do zmiennej *LeftValueY* -> Wysokość Mapy -1 (Array liczy się od 0 do podanego rozmiaru -1 [taka informacja dla mniej technicznych] ).
Następnie w pętli (ta sama pętla będzie też używana do nakładania wartości w array), o określonej liczbie powtórzeń (liczba z parametru) generowałem pokoje w zakresie od 20 [Stała Minimum] do LeftValue. Dodatkowo, wartość LeftValueY zmniejszałem o tą wylosowaną liczbę. Jedyny wyjątek, co do rozmiaru był wtedy, gdy LeftValue był mniejszy niż stała minimum.

private static int GetWallSize(ref int left)
{
  
    if (left <= MinimumWallSize)
    {
        return left;
    }
    var randomX = Random.Range(MinimumWallSize, left/2);

    left -= randomX;

    return randomX;
}

Takim o to sposobem dostaliśmy, wysokość, na której będzie rysowana linia pozioma.
A jest to sposób bardzo prosty 🙂

private static void GetHorizontalWall(MapElement[,] map, ref int posY, int ySize)
{
    posY += ySize;
    var door = Random.Range(1, MapSizeX-2);
    for (var x = 0; x < MapSizeX - 1; x++)
    {
        if (x >= MapSizeX - 1 || posY >= MapSizeY - 1) continue;
        if (x==door)
            map[x,posY] = MapElement.Door;
        else
            map[x, posY] = MapElement.Wall;
    }
}

Tradycyjny for od 0 do szerokość mapy pomniejszonej o 1 i wstawienie elementu ściany na określonej pozycji.
🙂 I takim oto sposobem mamy ładną mapkę z poziomymi liniami 🙂 czyli określoną liczbę poziomów. 🙂

LifeLike
Ściany Poziome

Linie Pionowe!

A żeby nam w głowie się nie poprzewracało od zbyt dużej przestrzeni, podzielimy każde “piętro” na mniejsze pokoiki. Procedura generowania ścian pionowych nie różni się niemal w ogóle od procedur generowania lini poziomych.

private static void GetVerticalWall(MapElement[,] map, ref int posX,  int posY, int xSize, int ySize)
{
    posX += xSize;
    var door = Random.Range(posY, posY + xSize);
    for (var y = posY; y < posY + ySize; y++) { if (y >= MapSizeY - 1 || posX >= MapSizeX - 1) continue;
        if (y==door)
            map[posX,y ]= MapElement.Door;
        else
            map[posX, y] = MapElement.Wall;
    }

}

W obecnej metodzie tworzymy zmienną LeftValueX, której przypisujemy wartość równą szerokości mapy pomniejszoną o 1. Wartość ta będzie się resetować przy każdym poziomie. W pętli wewnątrz procedury, mamy tę samą metodę, co wcześniej(GetWallSize) i zmniejszamy LeftValueX, generując określoną ilość ścian o losowych wymiarach. Różnica następuje jednak w nakładaniu tych linii na mapę. Tutaj pod uwagę bierzemy Wysokość obecnego poziomu ->
for od pozycji poprzedniej linii do pozycji następnej linii (y). A w następnym kroku wstawiamy ścianę z wykorzystaniem Map[pozycja linii pionowej, y] =MapElement.Wall;

LifeLike
Ściany Pionowe – te punkciki to drzwi

Losowość pokoi

Skoro już znamy metodę tworzenia ścian pionowych i poziomych, możemy teraz albo kontrolować ich ilość, albo pójść w większy element losowości, czyli generowanie ścian pionowych i poziomych 🙂 Wypadałoby, aby była choć jedna ściana pozioma, zatem zakres to (Range(1, Total), co do pionowych – możemy ustalić cienkie korytarze, więc zakres to Range(0, Total). I tak oto, rozstaw pokoi zaskoczyć może nawet i developera 🙂

Wrota

Czym byłyby pomieszczenia bez drzwi -> Wariatkowem 🙂 A tego tu osiągnąć nie chcemy. Żeby też nie było ich za dużo, ustalmy, że na ścianę poziomą wypada jedna para drzwi, tak samo jak po jednej na każdą pionową . Tak więc, będziemy mieli swobodny dostęp do pokoi na lewo i na prawo, ale już w dół lub w górę trzeba troszkę poszukać, gdyż są tylko jedne na poziom.
Oczywiście, można ulepszyć wg własnych widzi mi się.
Te dziwne, jasne kwadraciki w ścianach … to drzwi 🙂 Gdy dojdziemy do etapu poruszania postaci, a wraz z nią kamery zadbam o detale i większy ZOOM.

Łatwiejsza konfiguracja

Na koniec, do edytora graficznego dodałem edycję takich elementów jak:

  •  Wysokość Mapy,
  •  Szerokość Mapy,
  •  Maks. liczba ścian poziomych,
  •  Maks. liczba ścian pionowych.

Staram się zarządzać wszystkim z kodu, ale czasami dla eksperymentów, pokazów lub zarządzania teksturami, warto posłużyć się edytorem. Tym bardziej, że daje nam on wizualną możliwość ustawienia detali. 🙂

LifeLIke
Parametry Mapy

Podsumowanie

To tyle na razie pewnie w kwestii mapy. Powrócę do niej na 100% . W końcu mamy jeszcze dekorację, a nawet i efekty świetlne do dodania, plus jakąś scenografię inną niż szare tło. 🙂

Następny cel przed nami to: Postać, jej statystyki oraz poruszanie kamerą za postacią…….. !

P.S. kod jak zwykle dostępny na https://github.com/aluspl/RogueLikeDSP

LifeLIke
Developer Approved It!