MAPA ŚWIATA 2D
Ponownie witam wszystkich sympatyków OMEGI, ortodoksyjnych DELPHI-arzy jaki i tych co mają uraz do C. Czyli swoistego tłumaczenia wielu rozwiązań zapisanych w C na Pascala…
O czym będzie mowa w tej części?
Ano… o bardzo prostym pomyśle utworzenia pomniejszonego widoku świat 2D tak jak jest to widoczne na powyższym rysunku. Na mapie widoczny jest:
- świat 2D (bardzo odkrywcze 🙂
- żółty punkt odpowiadający położeniu gracza (związany jest z ruchem gracza)
- biały prostokąt odpowiadający obszarowi widocznej części świata (związany jest z ruchem żółtego punktu)
- czerwony prostokąt rysowany wokół punktu wskazanego lewym przyciskiem mychy (prawy go niszczy)
Cały kod omawiane klasy mapy(nie licząc deklaracji jej typu) zajmuje około 40 linijek i jest zapisany w pliku: OmegaMap.pas. Podaną klasę można oczywiście sobie rozbudować lub przerobić ją w komponent…
Uwagi związane z tworzeniem widoków mapy
Nasza mapa będzie potomkiem klasy TOmegaSurface. Oczywiści można to sobie zrobić przy pomocy wyświetlania rysunku bezpośrednio z OmegaImageList lub w inny sposób. Ja wybrałem takie rozwiązanie bo:
- tworząc odrębną klasę ma się większy porządek w głównym kodzie programu
- wprowadzenie ewentualnych poprawek odbywa się w pliku klasy a nie w głównym kodzie
- można łatwo utworzyć kilka potomków (pamiętajcie grozi nam niż demograficzny…)
Ponadto mapę wyświetlam w całości. Oczywiście wcześniej trzeba sobie ją jakimś innym własnym programikiem utworzyć, przeskalować i zapisać do pliku. Chyba ze ktoś się uprze i w czasie rzeczywistym będzie rysować kolejno kafle świata 2D w odpowiednim pomniejszeniu. Ale należ pamiętać, że trzeba to zrobić co najmniej dla 2 warstw. Z prostego rachunku wnika, że będzie tego znaczna ilość. Przykład świat ma rozmiar 125×75 co dla dwóch warstw da 2x125x75=18750 operacji skalowania i 18750 operacji rysowania kafli. Chyba, że przeskalujemy sobie kafle wcześniej i załadujemy je do OmegImageLis to zostanie nam tylko 18750 operacji rysowania….Tak czy siak zawsze to pochłonie trochę czasu.
Nasza klas będzie sobie gotową mapkę pobierać z zasobu
Co zyskujemy z takiego rozwiązania?
Możemy bardzo szybko zmieniać mapę świata poprzez zmianę indeksu wycinanej klatki lub zmianę indeksu całego obrazu mapy. Kiedy tak zmiana może wystąpić? W tedy gdy przechodzimy do innego poziomu gry lub na przykład w obrębie jednego poziomu istnieje kilka równoległych światów. Moje obrazy map świata 2D zgadzają się tylko co do układu dróg i położenia karczmy. A to tylko da tego, że za każdym uruchomieniem programu pozycje drzew są losowane. Wniosek: najlepiej cały świat jest budować sobie w edytorze.
Jakie zadania wykonuje nasz klasa naszej mapy?
- na bieżąco rysuje położenie gracza
- rysuje prostokąt widocznego obszaru świata 2D
- reaguje na kliknięcie lewym/ prawym przyciskiem myszki
- mapa nie jest rysowana gdy gracz jest w karczmie
Co można dodać do klasy?
- położenia innych żywych obiektów
- przesuwanie świata 2D do wskazanego punktu kliknięcja w mapie (podobnie jak to zrobiłem w swoim edytorze światów 2D patrz www.delphi.ilion.pl lub jak to jest zrobione w profesjonalnych grach)
- ukrycie miejsc w których nie było jeszcze duszka gracza
- i wiele, wiele innych pomysłów
Jak utworzyć i wywołać naszą mapę w projekcie gry?
Tu omówię tylko nagłówki procedur i miejsce ich wywołania. Analizę kodu pozostawiam czytelnikowi.
- Miejsce i sposób tworzenia mapy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
procedure TForm1.FormCreate(Sender: TObject); begin //***CZĘŚĆ 6********* Mapa:=TMapa.Create(self,self.handle); //utwórz obiekt Mapa.UstawOmegaImageList(OmegaImageList1);//przypisz zasoby grafiki // ustaw skalę mapy w stosunku do oryginalnego rozmiaru swiata //umnie to 15,625 % czyli 0.15625 w obu osiach Mapa.UstawSkale(0.15625,0.15625);//procedura UstawSkale ma byc przed procedurą UstawParametry //ustaw parametry Mapa.UstawParametry(11,3,0,OmegaScreen1.Height-OmegaImageList1.ImageList.Items[11].TileHeight,12,18,64,32); Mapa.IdPunkt:=12;//INDEKS OBRAZU ZOLTEJ PLAMKI //******************** end; |
Jak widać w 5 liniach mamy utworzoną naszą mapę. Tu chciałbym zwrócić uwagę na prametry podawane do procedury Mapa.UstawParametr:
- 11 (parametr AIdObraz) to indeks obrazu map przechowywanych w OmegaImageList1
- 3 (parametr AidKlatka)to indeks wycinanej klatki mapy
- 0 (parametr ALeft) to współrzędna X lewego górnego rogu zaczepienia mapy
- OmegaScreen1.Height-OmegaImageList1.ImageList.Items[11].TileHeight (parametr ATop) to współrzędna Y lewego górnego rogu zaczepienia mapy
- 12 (parametr ARectCol) to liczba klatek widocznego świata w osi X , tu odpowiada wartości 800 div 64
- 18 (parametr ARectRow) to liczba klatek widocznego świata w osi Y , tu odpowiada wartości 600 div 32
- 64 (parametr AKlakaMapyWidth) to szerokość kafla siatki mapy
- 32 (parametr AKlakaMapyHeight) to wysokość kafla siatki mapy
Deklaracja nagłówka tej procedury:
1 |
procedure UstawParametry(const AIdObraz,AIdKlatka,ALeft,ATop,ARectCol,ARectRow,AKlatkaMapyWidth,AKlatkaMapyHeight:integer); |
Skoro już utworzyliśmy mapę to należy ją wyświetlić i śledzić ruch gracza. I zrobimy to w procedurze zegara gry. Czyli tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
procedure TForm1.OmegaTimer1Timer(Sender: TObject); begin .... ..... //***CZĘŚĆ 6********* //śledż gracza Mapa.Ruch(8+(-Warstwa0[0,0].x+gracz.X)*Mapa.SkalaX,8+(-Warstwa0[0,0].y+Gracz.Y)*Mapa.SkalaY); //pokaż mapę gdy gracz jest poza karczmą if not Gracz.fWBudynku then Mapa.Draw; //******************** .... .... end; |
W procedurze Mapa.Ruch pojawia się liczba 8. Ta liczba jest wynikiem umiejscowienia rogu mapy w obrazie klatki mapy (brzmi jak masło maślane…) Zobrazuję to tym rysunkiem:
Ciała procedury Mapa.Draw nie będę tu przytaczać. Jest ona bardzo prosta. Jej zadanie polega na rysowaniu obrazu wycinanej klatki mapy, rysowaniu żółtego prostokąta pozycji gracza jak i białego. W celach poglądowych umieściłem również zaznaczenie czerwoną ramkę obszaru klikniecia myszki w mapie. Procedura Mapa.MauseDawn jest wywołana w procedurze formatki TForm1.FormMouseDown.
No cóż prawdopodobnie jest to ostatni art z cyklu wędrówek w świecie 2D…Na pewno nie wyczerpuje to problemów i pomysłów na stworzenie gry 2D w oparciu o komponent OMEGI, ale dalsze ciągnięcie tematu w kolejnych częściach mija się z celem.
Pytanie dlaczego?
Dlatego gdyż nie potrzebnie rozbudowuje się kod z każdą nową częścią. A to utrudnia jego analizę. Lepiej jest rozważać modele rozwiązań w mniejszych, niezależnych partiach. Poza tym chcąc napisać dobry silnik 2D , należy przebudować sposób wyświetlania warstw. Jak ktoś z czytelników widział mój programik Gigant2D ( www.delphi.ilion.pl dział narzędzia ) to na pewno zwrócił uwagę, że FPS osiąga wartości rzędu 190- 260 i to nie zależnie od wielkości świata czy to 30×30 czy tez 2000×2000 klatek dla trzech warstw. Moje rozwiązanie użyte w Gigancie tak samo będzie działać w izometrycznym świecie…. Model w omawianych części przyjąłem najprostszy bo nie jest on głównym tematem tych sześciu części.
Mam nadzieję, że te 6 części przyczynią się do popularyzacji OMEGI jak i DELPHI….
Czas kończyć bo zaczynam już wywyższać DELPHI:-)
Pozdrawiam oksal
5.06.2006 Zbylitowska Góra
Autor: oksal