Witam wszystkich czytających ten artykuł, w którym chciałbym podzielić się pewnym pomysłem, jaki można zastosować do gry 2D w przypadku jeśli chcemy podzielić się monitorem :). Spodziewany efekt końcowy widoczny jest na poniższym zrzucie działającego przykładu.
Oczywiście omawiane rozwiązanie jest przedstawione w DELPHI plus OMEGA. Grafika do przykładu została pobrana z www.strefatg.be Komponenty, jakie będą potrzebne są widoczne na poniższym rysunku
Za podział ekranu na dwie części odpowiedzialne są te zaznaczone – czyli OmegaSurface.
Ale po kolei.
1. Ustawiamy parametry podziału ekranu.
Przyjmujemy rozdzielczość na 800×600 (ustawiamy to w Object Inspector komponentu OmegaScreen), stąd każdy z graczy będzie mieć do dyspozycji ekran o wymiarach 400×600 pikseli. Załatwimy to w tej procedurze:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
procedure TForm1.FormCreate(Sender: TObject); begin OmegaScreen1.Init; OmegaScreen1.SetHWnd(self.handle); //parametry ekranu lewego OmegaSurface1.SethWnd(self.handle); OmegaSurface1.Width :=400; OmegaSurface1.Height:=600; OmegaSurface1.x:=0; OmegaSurface1.y:=0; //parametry ekranu prawego OmegaSurface2.SethWnd(self.handle); OmegaSurface2.Width :=400; OmegaSurface2.Height:=600; OmegaSurface2.x:=401;//przesuń o 1 piksel aby powstała linia graniczna OmegaSurface2.y:=0; OmegaFont1.Init; OmegaFont1.CreateFont('Arial',9,[]); OmegaFont1.Color:=OmegaColor(255,0,0); end; |
Tu chciałbym zwrócić uwagę, że linię podziału uzyskamy w możliwie prosty sposób – współrzędną prawego ekranu ustawimy nie na 400 ale na 401. Czyli: OmegaSurface2.x:=401; O jej kolorze zadecydują parametry czyszczenia głównego (całego) ekranu : OmegaScreen1.ClearScreen(0,0,0,0), którą wywołujemy w „zegarze” aplikacji.
2. Algorytm rysowania pojedynczego ekranu można wykonać według kroków:
Czyść ekran
Ustaw świat dla gracza
Rysuj świat
Rysowanie na obu ekranach odbywa się w tej procedurze (pełna postać w kodzie przykładu):
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
procedure TForm1.OmegaTimer1Timer(Sender: TObject); begin ..... .... OmegaScreen1.BeginRender; OmegaScreen1.ClearScreen(0,0,0,0); ...... ...... //Ekran GraczA OmegaSurface1.BeginRender; OmegaScreen1.ClearScreen(0,0,0,0); UstawSwiatWedlugGracza(GraczA); OmegaSprite1.Draw; OmegaSurface1.EndRender; //Ekran GraczB OmegaSurface2.BeginRender; OmegaScreen1.ClearScreen(0,0,0,0); UstawSwiatWedlugGracza(GraczB); OmegaSprite1.Draw; OmegaSurface2.EndRender; ...... ...... OmegaScreen1.EndRender; end; |
Dlaczego OmegaSprite1.Draw? a to tylko dla tego, że nasz świat oparty jest na kostkach (20×20) klasy TSprite. Jeśli to rozwiązanie zastosujemy to nie musimy w pętli rysować naszego świata (załatwi to za nas procedura Draw komponentu OmegaSprite) i mamy bazę wyjściową do testowania kolizji (której w tym przykładzie nie uwzględniam). Oczywiście możemy zastosować komponent OmegaMap na podobnych zasadach lub możemy obsłużyć rysowanie świata w pętli korzystając z OmegaImageList…Te problemy są na inny temat.
Ważnym elementem w naszych rozważaniach jest ta procedura: UstawSwiatWedlugGracza(). Jak ona działa?
3. Działanie procedury: UstawSwiatWedlugGracza()
Zadaniem tej procedury jest odpowiedni przesuwać świat w ekranach obu graczy w uwzględnieniem zmiany pozycji gracza pierwszego względem gracza drugiego. Inaczej mówiąc typowe zastosowanie transformacji Galileusza- znajdź współrzędne poruszającego się układu w innym poruszającym się układzie…
Ciało wspomnianej procedury:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
procedure TForm1.UstawSwiatWedlugGracza(Gracz:TGracz); var k,w:integer;//to kolejne kolumny i wiersze begin for w:=0 to high(Warstwa0)do for k:=0 to high(Warstwa0[0])do begin //wartswa gruntu Warstwa0[k,w].x:=Gracz.EkranX+Gracz.xMapy+k*OmegaImageList1.ImageList.Items[0].ImageWidth; Warstwa0[k,w].y:= Gracz.yMapy+w*OmegaImageList1.ImageList.Items[0].ImageHeight; ..... //wartswa krzaków itp. .....Warstwa1[k,w].x:=Warstwa0[k,w].x; Warstwa1[k,w].y:=Warstwa0[k,w].y; end; if Gracz.Name='GraczA'then begin Gracz.x:=Gracz.X0; Gracz.y:=Gracz.Y0; GraczB.X:=GraczB.X0+Gracz.xMapy-GraczB.XMapy; GraczB.Y:=GraczB.Y0+Gracz.yMapy-GraczB.YMapy; end; if Gracz.Name='GraczB'then begin Gracz.x:=Gracz.X0+Gracz.EkranX; Gracz.y:=Gracz.Y0; GraczA.X:=GraczA.X0+Gracz.EkranX+Gracz.xMapy-GraczA.XMapy; GraczA.Y:=GraczA.Y0+Gracz.yMapy-GraczA.YMapy; end; end; |
Na uwagę zasługują te linie:
1 2 3 |
Warstwa0[k,w].x:=Gracz.EkranX+Gracz.xMapy+k*OmegaImageList1.ImageList.Items[0].ImageWidth; Warstwa0[k,w].y:= Gracz.yMapy+w*OmegaImageList1.ImageList.Items[0].ImageHeight; |
Gdzie zmienna Gracz.EkranX to stała wartość względnego przesunięcia obu ekranów. Wynosi ona 400 pikseli wzdłuż osi X. Na osi Y – to 0. Jest ona ustalana w momencie tworzenia graczy. Dla gracza lewego podajemy zero a dla gracza prawego 400 (patrz do procedury tworzenia gracza w kodzie programu). Zmienne Gracz.xMapy i Gracz.yMapy przechowują informację o ile pikseli przesuną się każdy z graczy w swoim świecie. Ponadto należy zwrócić uwagę na to, że gracz w swoim ekranie wyświetlany jest na środku, to świat jest przesuwany względem „duszka” gracza.
Jako, że ta sama procedura została zastosowana dla dwóch graczy użyłem najprostszego pomysłu ich rozróżnienia- po nazwie własnej (GraczA, GraczB).
Omawiana procedura jest wywołana dwa razy, podczas odświeżania ekranu lewego a następnie ekranu prawego.
I to by było na tyle o tworzeniu dwóch ekranów. Pozostałe elementy kodu programu to procedury tworzenia losowej mapy świata, duszków graczy i klawiszologia . Omawiany przykład nie uwzględnia zmiany współrzędnej „Z” duszka, kolizji, pilnowania dopuszczalnej granicy ruchu itp. Problemy te są przedstawione w przykładach dołączonych do komponentu Omegi.
Na zakończenie mogę jeszcze wspomnieć, że ten pomysł można zastosować do rysowania mapy świata gry. Tylko należy utworzyć jeden pomocniczy mały ekran o wymiarach przeskalowanego świata (gra była by rysowana w OmegaScreen1 zaś mapka w OmegaSurface). Choć myślę że nie jest to jedyne rozwiązanie…Ale od czego są pomysły :). No i oczywiście jak się pomyśli to można znacznie przyśpieszyć rysowanie światów i to ogromnych światów. Może kiedyś zdradzę swój pmysł…
Pozdrawiam oksal
Autor: oksal