[POBIERZ] kod źródłowy i inne pliki potrzebne do uruchomienia gry  [POBIERZ] 

Zachęcam do komentowania :) Mile widziane sugestie i pytania, na które chętnie odpowiem

5 wrz 2010

Bronie w grze- poziom amunicji

W grze mając do dyspozycji jakieś bronie ważym elementem jest ich amunicja. W mojej grze jak wcześniej pisałem występują 4 rodzaje broni:

1-bazooka
2-karabin maszynowy
3-laser
4-miotacz ognia 


na blogu jak we wcześniejszych postach będę się posługiwał tymi obiektami z poniższymi kolorami:

Cshots strzal_bazooka;
Cshots strzal_machinegun;
Cshots strzal_laser;
Cshots strzal_flamethrower;

Na początku w pliku klasy.h deklarujemy sobie zmienne, które będą przechowywały stan amunicji i definiujemy je w main() przykładowymi wartościami:

ammo_bazo=100;
ammo_machgun=30;
ammo_laser=20;
ammo_flame=10;

 

W grze maksymalny stan amunicji będzie wynosił 100. Poniżej przedstawiam znany fragment kodu odpowiedzialny za strzał poszczególną bronią. Widzimy nowe linijki:

ammo_bazo--;
ammo_machgun--;
ammo_laser=--
ammo_flame=--

Oczywiście oznaczają one zmniejszenie zmiennej o 1, gdy wystrzelimy pocisk. 

Zmieniłem także 4 instrukcje warunowe if np:
if( (bron==1) && (ammo_bazo>0) ) 
Oznacza, że możemy oddać strzał jeżeli mamy wybraną broń bazooka(klawisz 1) i poziom amunicji jest dodatni. 


if( key[KEY_SPACE]) 
{

   if( (bron==1) && (ammo_bazo>0) )
   {
       if( (strzal_bazooka.pozpoc_y<0) || (strzal_bazooka.pozpoc_y==0) )
      {
          strzal_bazooka.strzelaniee(ludek.pozs_x, ludek.pozs_y ); 
          play_sample(dzwiek, 200,200,1000,0);  
          ammo_bazo--;
      }
   }

   if( (bron==2) && (ammo_machgun>0) )
   {
       if( (strzal_machinegun.pozpoc_y<0) || (strzal_machinegun.pozpoc_y==0) )
       {
          strzal_machinegun.strzelaniee(ludek.pozs_x, ludek.pozs_y ); 
          play_sample(machingunn, 200,200,1000,0);  
          ammo_machgun--;
       }   
   } 

   if( (bron==3) && (ammo_laser>0) )
   {
       if( (strzal_laser.pozpoc_y<0) || (strzal_laser.pozpoc_y==0) )
       {
          strzal_laser.strzelaniee(ludek.pozs_x, ludek.pozs_y ); 
          play_sample(laserr, 200,200,1000,0);  
           ammo_laser--;
       }
   } 

   if( (bron==4) && (ammo_flame>0) )
   {
        if( (strzal_flamethrower.pozpoc_y<0) ||(strzal_flamethrower.pozpoc_y==0) )
       {
          strzal_flamethrower.strzelaniee(ludek.pozs_x, ludek.pozs_y ); 
          play_sample(flamethrow, 200,200,1000,0);  
          ammo_flame--;
       }
   } 
}

Zmianie uległ(co pewnie nikogo nie dziwi :p)  interfejs gry:

Widzimy jakies 4 znaczki. Onaczać będą amunicję w grze. Od góry:
bazooka, karabin maszynowy, laser i miotacz ognia.
Celowo widzimy biały pasek, ponieważ ten pasek będzie pokryty czerwonym zależnie od poziomu amunicji poszczególnej broni. 

Odpowiadać za to będzie funkcja:

void poziom_ammo(int poz_ammo, BITMAP* bitmapa, int wysokosc)
{
   for(int j=0; j<=poz_ammo ; j++)
   {
       if(poz_ammo!=0)
       {
           draw_sprite(bufor, bitmapa, 109+j, wysokosc);
       }
   }
}



1- argumentem w funkcji jest przesyłana zmienna zawierająca stan amunicji
2- argumentem w funkcji jest wskaźnik do bitmapy
3- argumentem w funkcji jest wysokość na jakiej ma być wyświetlany pasek dla poszczególnej broni.

Jak już wspomiałem będziemy rysować czerwne linie na białym pasku w interfejsie:

draw_sprite(bufor, bitmapa, 109+j, wysokosc);  

109 -tyle pikseli od lewej rozpoczyna się biały pasek i mierzy on 100 pikselidługości i 2 szerokości.
109+j będziemy pasek zwiększali(lub zmiejszali) o j-pikseli tzn. o tyle ile będzie miała przesyłana zmienna poz_ammo.

W main wywołujemy ją tak:

poziom_ammo(ammo_bazo, menudolne_poz_ammo, 523);
poziom_ammo(ammo_machgun, menudolne_poz_ammo, 543);
poziom_ammo(ammo_laser, menudolne_poz_ammo, 562);
poziom_ammo(ammo_flame, menudolne_poz_ammo, 583);

  
Ten element dodaje sensu grze, lecz nie jest do końca dopracowany (jeżeli chodzi o kod). Nie miałem pomysłu jak to lepiej zrobić i zrobiłem ten element funkcyjnie, a nie obiektowo ale znając życie kod przejdzie NAPEWNO modyfikację i będzie obiektowo. Narazie ważne aby działało i motywowało do dalszej pracy :)
W grze dodałem też kolizję pocisku robocika(miotacza ognia) z pociskiem wroga co umożliwia robocikowi jakąś obronę. Nie będę po raz kolejny pisał i objaśniał kodu na blogu, ponieważ jest do ściągnięcia wraz z plikami potrzebnymi do uruchomienia gry (ver 1.04).

Gra na tym etapie wygląda następująco: 

1 komentarz:

  1. Ktoś przerobi na obiektowo... oczywiście :P bardzo proszę ;)

    Wg mnie najlepszym rozwiązaniem jest zrobić tak jak już wcześnie pisałem :
    - Stworzyć enum TypBroni i w nim dać bazooke,jakieś miotacze itd

    - Stworzyć Listę/Tablicę wszystkich broni

    - w Main stworzyć pole aktywnaBron, która będzie typu (enum)TypBroni

    - całą metode ze sprawdzeniem i odjęciem amunicji przenieść do ciała klasy

    Wtedy jak wcześnie pisałem kod będzie bardzo prosty :

    if(KEY[SPACE])
    {
    ListaBroni[(int)aktywnaBron].Strzel();
    }



    kawałek kodu z klasy Bron :
    class Bron
    {
    private int _poziomAmunicji = 50;

    public void Strzel()
    {
    ...odtwórz dźwięk
    if(this._poziomAmunicji > 0) this._poziomAmunicji--;
    ..inne funkcje dotyczące trzału
    }
    }


    Jak widać wszystko zależy od rozplanowania. Możesz wymyślić sobie, że np. klasa Bazooka dziedziczy z klasy Bron, bo np. Metoda Strzel w Bazooce wygląda trochę inaczej niż w np. MiotaczuOgnia, ale każda z broni ma pole z liczbą amunicji. Wtedy możemy zadanie rozwiązać na dwa sposoby.

    -----wtrącenie-------
    W skrócie Bazooka:Bron lub MiotaczOgnia:Bron, wtedy w bazooce można na stałe przypisać wartość do pola z Broni, nie trzeba tego robić przez np. konstruktor
    -----//wtracenie------

    Sposób 1. tworzymy dodatkową metodę za pomocą której odejmiemy amunicję, minimalnie więcej pisania

    class Bazooka
    {
    private int _poziomAmunicji = 40;

    public void OdejmijAmunicje()
    {
    if(this._poziomAmunicji > 0)
    this._poziomAmunicji--;
    }
    }

    class Bazooka:Bron
    {
    bla bla bla bla
    }

    SPOSÓB WYWOŁANIA
    if(KEY[SPACE])
    {
    ListaBroni[(int)aktywnaBron].Strzel();
    ListaBroni[(int)aktywnaBron].OdejmijAmunicje();
    //bądź w funkcji strzel bazooki wywolujesz OdejmijAmunicje, co jest chyba bardziej logiczne
    }

    _________________________________________________

    Opcja druga troszkę ciekawsza jeśli obiekt klasy Bron nie istnieje jako tako, powinno się klasę oznaczyć jako abstract, co daje nam pewne rozszerzenie ;]

    abstract class Bron
    {
    private int _poziomAmunicji = 40;

    public virtual void Strzel()
    {
    if(this._poziomAmunicji > 0)
    this._poziomAmunicji--;
    }
    }

    Teraz klasa Bazooka dziedziczy z Bron czyli class Bazooka:Bron
    {
    public override void Strzel()
    {
    zrób metody specyficzne dla Bazooki
    base.Strzel(); // rowna się jakby Bron.Strzel();
    }
    }

    wtedy
    if(KEY[SPACE])
    {
    ListaBroni[(int)aktywnaBron].Strzel();
    //kod zostaje ten sam ;)
    }

    _________________________________________________

    To w sumie tyle i to jest moja interpretacja przedstawionych metod. Obiektowość jest ciekawa i chciałem Cie zachęcić do jej dalszego poznawania, wbrew pozorom jest to ładne, lecz nie zaprzeczam, potrzeba trochę czasu na oswojenie się ze wszystkim. Z góry przepraszam jesli w c++ robi się coś inaczej niż w c#, jedyna chyba róznica, to że trzeba zastąpić C# znak . na znak ->

    OdpowiedzUsuń