Odsłona czwarta – ta która nie była przewidziana.
Bohater trylogii wędrówki w świecie 2D w końcu znalazł w lesie karczmę, o której słyszał w części drugiej. Wie, że w karczmie jest to co musi być…
Ale nie umie do niej wejść. Musimy mu pomóc.
Czas zacząć. Na wstępie przedstawię pewne fakty wynikające z przyjętego modelu rozwiązania organizacji świata 2D opisanego w pierwszych trzech częściach:
- Jeśli bohater jest za drzewem lub budynkiem to ma być widoczny ale w zmienionych barwach
- Rozpoznaje pnie drzew- umie wykryć kolizję z pniem
- Umie chodzić tylko po ścieżkach (w tej części zrezygnuję z tej umiejętności, patrz procedura: TGracz.MoznaIsc; warunek : if(Drogi[wd,kd]>-1){or(MapaWarstwa0[w,k]=0)})
Do tego modelu należy dodać kolejne warunki:
- W świecie 2D może (raczej musi) istnieć budynek, który osadzamy w całości (w jednym obrazie graficznym)
- Do budynku można wejść- czyli musi być oprogramowany model wirtualnych drzwi
- Warunek b wymaga wprowadzenia zmiany mapy w świecie 2D w obrębie aktualnego poziomu gry
- Warunek c musi być przeprowadzony możliwie najszybciej, tak aby nie spowalniać gry
- Jeśli bohater jest w budynku, to nadal obowiązuje test kolizji (aby czasem go z karczmy przez ścianę nie wykopali)
Tu chciałbym zwrócić uwagę na pewną trudność warunku c. Otóż jeżeli bohater jest poza budynkiem, to wymiary budynku zajęte w mapie siatki świata 2D nie są duże w porównaniu z układem pokoi , komnat itp. jakie mogą się znaleźć w tej budowli. Powstaje problem szybkiej organizacji zmiany siatek mapy, grafiki…
Przerażający problem jeśli się nie ma na to pomysłu. Rozwiązanie, które podam jest proste i bardzo szybkie. Ponadto łatwo ten schemat można zastosować do innego modelu warstw (w tym gigantycznych map).
Osadzamy budynek- upragnioną karczmę
Jako, że „duszki” bohaterów mają być widoczne za ścianami budynku – czyli zmieniać się na tych samych zasadach co za drzewami przyjmę, że budynek karczmy będzie „dzieckiem” klasy Troslina. Czyli TKarczma=class(TRoslina).Do zasobów grafiki komponentu OmegaImageList należy dodać obraz budynku karczmy oraz obraz „kości” jego wnętrza.
Naszą karczmę należy gdzieś w świecie umieścić. Do unitu: swiatUnit2.pas wprowadzimy tablicę przechowującą punkt zaczepienia budynków (u nas tylko karczmy):
1 |
mapaWarstwa1:array[0..19,0..19]of integer ; |
Tę tablicę mozna traktować jako tablicę murów. Czego nie było w częściach poprzednich. Dodatkowo ten punkt będzie wyznaczać wirtualne drzwi do karczmy. Położenie punktu w tej tablicy to 9 kolumna i 14 wiersz licząc od zera. W tych współrzędnych umieszczamy indeks odpowiadający indeksowi obrazowi naszej karczmy pobranemu z OmegaImageList1. Czyli jest to wartość 7.
Tu nie musimy się martwić, że obraz budynku karczmy będzie justowany do jej lewego boku w klatce mapy świata. Ponieważ TKraczma jet potomkiem klasy TRoslina. A w tej klasie, w procedurze rysowania obrazu jest narzucane justowanie względem środka klatki mapy. Stąd należy obraz drzwi karczmy narysować (w miarę) na środku jej ściany.
Możemy teraz w kodzie programu utworzyć nasza karczmę. Zrobimy to w procedurze tworzenia świata 2D.
Czyli
1 |
procedure TForm1.TworzSwiat; |
Tu chciałbym zwrócić uwagę na te linijki kodu tej procedury:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
...... Karczma.DoCollision :=true;//false; Karczma.DoPixelCheck:=true; //zapamietaj obszar karczmy Drogi[wD,kD-1]:=7; //Drogi[wD,kD]:=7; Drogi[wD,kD+1]:=7; Drogi[wD-1,kD-1]:=7; Drogi[wD-1,kD]:=7; Drogi[wD-1,kD+1]:=7; .... |
W przyjętym rozwiązaniu testu kolizji musimy zabronić bohaterowi wejścia do karczmy z każdej strony. Czyli uzupełnić tablicę Drogi indeksami większymi od –1 (minus jeden) Tak aby w ogólnym zarysie powstał obszar zabroniony (za wyjątkiem drzwi) odpowiadający kształtowi budynku karczmy. Powiedzmy coś takiego:
Rola tablicy Drogi omówiona jest w częściach poprzednich.
Wchodzimy do karczmy
Teraz wystarczy dojść do współrzędnych wirtualnych drzwi. Czyli w
1 |
procedure TGracz.JakaKostka; |
należy odnotować fakt wejścia/ wyjścia z budynku. Modyfikujemy wspomnianą wyżej procedurę pprzez dodanie tych linijek:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//*****część czwarta************ if (k=9) and (w=14) and (not fWBudynku) then begin fWBudynku:=true; wKarczmie; end; if (k=9) and (w=15) and (fWBudynku) then begin fWBudynku:=false; zKarczmy; end; end; |
Teraz kilka słów o jej działaniu.
Proszę zauważyć flagę logicznego stanu obecności „duszka” w budynku fWBudynku. Ta flaga będzie decydować o ukryciu obiektów nie związanych z wnętrzem karczmy. Czyli :obrazu karczmy, drzew i trawy. Jak tę flagę szybko w kodzie programu wykorzystać?
Skorzystamy z faktu, że obraz Karczmy i drzew to jedna i ta sama klasa macierzysta TRoslina żyjąca na obiektach TSprite. Aby unikać niepotrzebnych pętli, które zawsze spowalniają korzystamy z faktu, że procedury obiektów TSprite są wykonywane w zegarze aplikacji w liniach:
1 2 3 4 5 |
OmegaSprite1.Dead; OmegaSprite1.Move(OmegaTimer1.DeltaSecs); OmegaSprite1.Draw; |
Więc dla klasy TRoslina wprowadzimy dodatkowa linię:
1 2 3 4 5 6 7 |
procedure TRoslina.Move(const MoveCount:single); begin if TGracz(Gracz).fWBudynku then Visible:=false else Visible:=true; inherited Move(MoveCount); |
oraz
1 2 3 4 5 |
procedure TRoslina.onCollision(const Sprite:Tsprite;const colX,colY:integer); begin if not Visible then exit; //część czwarta |
Rozwiązanie proste, skuteczne i szybkie.
Dla trawy zrobimy inaczej. Nie możemy ukryć warstwy podstawowej. Zostaje nam zmiana obrazów klatek.
Zrobią to poniższe procedury:
1 2 3 |
procedure wKarczmie;//część czwarta procedure zKarczmy; //część czwarta |
Nie będę tu ich przytaczać, można je prześledzić w kodzie programu. Ich działanie jest proste. Jedynie omówię w punktach pomysł jaki tu wykorzystałem:
- Zapamiętaj siatkę dróg w buforze (warto zapamiętać niż ponownie ją odtworzyć – zysk na czasie)
- Zbuduj nowa siatkę dróg obowiązującą w budynku
- Ustaw w warstwie zerowej indeks -1(minus jeden) dla klatek obrazu (w poprzednich częściach wyjaśniłem co robi takie podejście)
- W klatkach odpowiadających podłodze budynku załaduj nowe obrazy na podstawie mapy siatki budynku. Tu: mapaKarczmaWarstwa0
- Utwórz warstwę nr 1 na podstawie siatki mapy wnętrza budynku. Tu: mapaKarczmaWarstwa1 Uwaga tworzymy tylko tyle ile jest kostek ścian. Nie ma sensu tworzenie tyle kostek mapy dla warstwy 1 (pierwszej) ile wynosi całkowity rozmiar świata. Ten pomysł też przyczynia się do wzrostu prędkości zmiany i wyświetlania grafiki gry.
Jeżeli bohater wyjdzie z budynku to zwalniamy warstwę nr jeden i przywracamy stan poprzedni. Wszystko odbywa się bardzo szybko i w miarę prosto bo nie ma dodatkowych indeksów zmian, flag i pętli. Warunkiem dobrej prędkości jest również trzymanie załadowanej grafiki w OmegaImageList obowiązującej w danym poziomie gry. Pomysł można usprawnić tak aby tablice warstw, wnętrz budynków przechowywały również indeksy obrazów a nie tylko ich klatek. Ja tu tego nie wprowadzam a to tylko ze względu na zgodność z modelem części 1,2 i 3 omawianych artykułów oraz z rozrostem map siatek (budowanie ich z klawiatury w kodzie programu mija się z celem, to musi robić nam edytor).
I to by było na tyle o piciu piwa w karczmie świata 2D
Pozdrawiam oksal.
Zbylitowska Góra 15.04.2006