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: 


Ktoś przerobi na obiektowo... oczywiście :P bardzo proszę ;)
OdpowiedzUsuń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 ->