Witam, w czwartej odsłonie artykułu poświęconego tworzeniu gier platformowych. Podczas tej lekcji damy bohaterowi gry możliwość strzelania kulami ognia oraz sprawimy, aby gra działała poprawnie również na szybszych procesorach niż 400 Mhz.
1.Ograniczenie FPS
Na początku zajmiemy się tą drugą sprawą. Aby ograniczyć FPS w grze wystarczy w obiekcie TPowerTimer (Zegar) :
ustawić FPS na 120
uaktywnić MayProcess (True)
przenieść dotychczasową zawartość procedury OnRender do OnProcess
Poszukać w tej procedurze polecenia :
1 2 3 |
PowerFont1.TextOut('FPS: '+IntToStr(Zegar.FrameRate),10,10, clWhite,EffectNone); |
i zamienić je na :
1 2 3 |
PowerFont1.TextOut('FPS: '+IntToStr(Zegar.FPS),10,10, clWhite,EffectNone); |
W ten sposób na nowszych komputerach gra nie będzie zbyt szybko chodzić. Ograniczyliśmy FPS do 120, co powinno zapewnić nam płynność gry.
2.Kule ognia – wprowadzenie możliwości strzelania
Najpierw utwórzmy nowy unit plikTPocisk. Będzie on zawierał wszystkie polecenia i procedury potrzebne do obsługi pocisków. Kolejnym krokiem jest zadeklarowanie innym unitów w uses oraz stworzenie struktury obiektu pocisku. Przedstawia się to następująco :
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 |
unit plikTPocisk; interface uses PowerTypes, PowerDraw3,PDrawEx, AGFUnit, d3d9, VTDbUnit; type TPoz = record X : Integer; Y : Integer; end; // Obiekt ognia wystrzelonego przez gracza type TPocisk = object Pozycja : TPoz; // pozycja w pikselach PozycjaNaMapie : TPoz; // pozycja w tablicy Sila : Byte; // sila uderzenia GrafikaL, GrafikaP : TAGFImage;// grafika pocisku AnimPos : Cardinal; // klatka animacji Kierunek : Byte; // kierunek lotu W_Locie : Boolean; // sprawdza czy aktywny procedure Tworz; // inicjacja pocisku procedure Wystrzel(X1, Y1 : LongInt; Kierunek1, Sila1 : Byte); // wystrzal procedure Zniszcz; //niszczy pocisk procedure Rysuj; // rysowanie na ekran procedure Oddech; // mechanizmy kontrolne pocisku end; implementation uses plikTGra, plikTGracz, zmienne; |
Jak zapewne zauważyliście struktura jest bardzo podobna do struktury gracza. Pocisk nie jest niczym innym jak takim graczem, tylko bez możliwości kontroli i trochę innych poleceniach.
Pociski będziemy tworzyć podczas uruchamiania gry, aby nie ładować co wystrzał pocisku jego grafiki od nowa. Procedura poniżej wczytuje animację pocisku odwróconego w lewo oraz w prawo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
(* inicjacja pocisku *) procedure TPocisk.Tworz; begin //---------W lewo---------- GrafikaL := TAGFImage.Create(PD); GrafikaL.LoadFromVTDb(Gra.GrafikaBaza, 'pocisk1_L', D3DFMT_A8R8G8B8); //---------W prawo---------- GrafikaP := TAGFImage.Create(PD); GrafikaP.LoadFromVTDb(Gra.GrafikaBaza, 'pocisk1_P', D3DFMT_A8R8G8B8); end; |
Poniższa procedura uaktywnia pocisk według podanych parametrów. Jest ona wywoływana w procedurze Idz obiektu TGracz po wciśnięciu przycisku Space.
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 |
(* wystrzal pocisku *) procedure TPocisk.Wystrzel(X1, Y1 : LongInt; Kierunek1, Sila1 : Byte); begin Kierunek := Kierunek1; // kierunek lotu Sila := Sila1; // siła uderzenia Pozycja.X := X1; //pozycja Pozycja.Y := Y1; W_Locie := True; // uaktywnienie //Polecenie przesuwa punkt początkowy pocisku na lewą lub prawą stronę gracza If kierunek = w_lewo then dec(pozycja.x,grafikaL.PatternWidth); If kierunek = w_lewo then inc(pozycja.x,grafikaL.PatternWidth); end; |
Mamy już utworzony obiekt, nadszedł czas, aby było go widać na ekranie, do tego celu posłużymy się procedurą :
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 |
(* rysowanie na ekran *) procedure TPocisk.Rysuj; begin //rysuje pocisk odwrócony w lewo If kierunek = w_lewo then Gra.Draw(GrafikaL, Pozycja.X-(mapa_X*50), Pozycja.Y, AnimPos, EffectSrcAlpha); //rysuje pocisk odwrócony w prawo If kierunek = w_prawo then Gra.Draw(GrafikaP, Pozycja.X-(mapa_X*50), Pozycja.Y, AnimPos, EffectSrcAlpha); //po wciśnięciu Entera zostanie zaznaczona aktualnie //zajmowana klatka If PokazZajmowaneKlatki then PD.FillRect(50*(PozycjaNaMapie.X-mapa_x), 50*(PozycjaNaMapie.Y),50,50,$0000FF,EffectAdd); end; |
Teraz pozwólmy „oddychać” pociskowi, aby wiedział co ma robić :
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 52 53 54 55 56 57 58 59 60 61 62 63 |
(* mechanizmy kontrolne pocisku *) procedure TPocisk.Oddech; var i:byte; begin //przesuwanie klatki animacji Inc(AnimPos); //jesli dojdzie do końca wróc do pierwszej klatki If AnimPos > GrafikaL.PatternCount then AnimPos := 0; //określanie pozycji na mapie PozycjaNaMapie.X := (Pozycja.X+ (GrafikaL.PatternWidth) div 2) div 50; PozycjaNaMapie.Y := Pozycja.Y div 50; //przemieszczanie pocisku zależnie od kierunku If kierunek = w_lewo then dec(pozycja.x,4) else inc(pozycja.x,4); //niszczenie pocisku do zniknięciu z ekranu If (pozycja.x-(mapa_x*50) > PD.Width)or (pozycja.X < -grafikaL.PatternWidth) then Zniszcz; //sprawdzanie, czy pocisk nie znajduje się na wrogim graczu, //jeśli tak to ma go zniszczyć i zniszczyć samego siebie for i:=0 to 10 do If Wrogowie.StanZycia <> st_martwy then If (Wrogowie.PozycjaNaMapie.X = PozycjaNaMapie.X) and(Wrogowie.PozycjaNaMapie.Y = PozycjaNaMapie.Y) then begin Wrogowie.StanZycia := st_wybucha; Zniszcz; end; end; |
Na zakończenie ostatnia procedura – niszcząca pocisk:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
(* Niszczy pocisk *) procedure TPocisk.Zniszcz; begin W_Locie := False; Pozycja.X :=0; Pozycja.Y :=0; PozycjaNaMapie.X :=0; PozycjaNaMapie.Y :=0; Sila :=0; Kierunek :=0; AnimPos :=0; end; |
W ten sposób dotarliśmy na koniec naszego nowego unitu. Pozostało jeszcze tylko parę drobiazgów poza nim. Zerknijmy do unitu Zmienne.pas, i w var dodajmy
1 |
Pociski : array[0..5] of TPocisk; |
Oczywiście liczbę pocisków można zwiększyć, ale moim zdaniem powinno to wystarczyć.
Teraz otwórzmy plikTGra i dodajmy w procedurze RysujGraczy :
1 2 3 4 5 |
for i:=0 to 5 do If Pociski.W_Locie then Pociski.Rysuj; |
W tym samym unicie znajdziemy procedurę Oddychaj, do niej dopiszmy :
1 2 3 4 5 |
for i:=0 to 5 do If Pociski.W_Locie then Pociski.Oddech; |
Tłumaczyć tych poleceń chyba nie muszę, pierwsza rysuje, następna wykonuje czynności danego przycisku. Pozostało nam jeszcze tylko polecenie obsługi przycisku spacji i okresowe wystrzeliwanie przycisku. Przejdźmy do pliku plikTGracz, tam w strukturze TGracz dodajmy dwie zmienne :
1 2 3 4 5 6 7 |
// pozycja czasowa CzekajSpacjaPos : Byte; // okres CzekajSpacjaOkres : Byte; |
W procedurze TGracz.Tworz przypisz im wartości początkowe :
1 2 3 |
CzekajSpacjaOkres := 30; CzekajSpacjaPos :=0; |
W tym samym unicie poszukaj procedury TGracz.Idz; Następnie poszukaj warunku dla gracza kierowanego przez człowieka :
1 2 3 |
IF (TRYB = tryb_czlowiek)and(StanZycia = st_zywy) THEN begin |
Po nim umieść :
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 |
If CzekajSpacjaPos < CzekajSpacjaOkres then inc(CzekajSpacjaPos); (* Wystrzal pocisku *) // jeśli pozycja czasowa będzie miała maksimum, // czyli wartość okresu i zostanie wciśnięta Spacja // wtedy ma wystrzelić pierwszą nie znajdującą się // jeszcze w locie kulę If (CzekajSpacjaPos = CzekajSpacjaOkres) and(Klawiatura.Keys[57]) then //Space begin for NumerPocisku := 0 to 5 do If not Pociski[NumerPocisku].W_Locie then begin Pociski[NumerPocisku].Wystrzel(Pozycja.X, Pozycja.Y, KierunekX_old, 1); CzekajSpacjaPos :=0; Break; end; end; |
Na tym zakończymy ten artykuł.
Autor: Nakiel