P015 - Prosta gra zręcznościowa - BOMBOWIEC
Programy uruchomiono w środowisku Bloodshed Dev-C++ 4.9.9.2

Uwaga, program p015 wykorzystuje bibliotekę newconio, którą stworzyliśmy na wcześniejszych zajęciach koła. Do projektu należy dołączyć plik newconio.cpp oraz plik nagłówkowy newconio.h. Bez tych plików program nie uruchomi się.

// I Liceum Ogólnokształcące
// im. K. Brodzińskiego
// w Tarnowie
//--------------------------
// Koło informatyczne 2006/7
//--------------------------
// Program: P015-01
//--------------------------

#include "newconio.h"
#include <iostream>

using namespace std;

// Procedura wyświetla ekran tytułowy gry
//---------------------------------------
void ekran_tytulowy()
{
  textattr(7); clrscr();
  textcolor(15); center(10,_pl("KOŁO INFORMATYCZNE '2006"));
  textcolor(14); center(12,"I LO w Tarnowie");
  textcolor(11); center(14,"B O M B O W I E C");
  textcolor(7);  center(24,_pl("--- Naciśnij dowolny klawisz ---"));
  while(!getch()) ;
}

// Procedura generuje miasto
//--------------------------
void generuj_miasto()
{
  textattr(0); clrscr();
  fillrectattr(0xb0,0,0,79,23);   // niebo nad miastem
  fillrectattr(0x20,0,24,79,24);  // trawnik pod miastem
  for(int i = 10; i < 70; i++)    // miasto
    fillrect('_',(i%6)<<4,i,23-rand()%10,i,23);
}

// Funkcja sprawdza, czy miasto zawiera jakieś budynki
//----------------------------------------------------
bool miasto_zburzone()
{
  for(int i = 10; i < 70; i++) if(getchxy(i,23) == '_') return false;
  return true;
}

// Procedura gry
//--------------
void bombarduj()
{
  int  wynik,sx,sy,bx,by;
  char key;
  
  wynik = sx = sy = 0;
  bx = by = 24;
  while(true) // Pętla zdarzeń
  {
    textattr(0xb1); gotoxy(sx,sy); cout << ">";
    if(kbhit())
    {
      while(!(key = getch())) ;
      if((by == 24) && (key == ' '))
      {
        by = sy+1; bx = sx;
      }
    }  
    if(by < 24)
    {
      textattr(0xbc); gotoxy(bx,by);
      if(getchxy(bx,by) == ' ')
        cout << '*';
      else
      {
        cout << char(177);
        wynik++;      
      }
    }
    textattr(0x2e); gotoxy(0,24); cout << wynik;
    delay(70);
    if(by < 24)
    {
      putchxy(' ',bx,by);
      by++;
    }
    putchxy(' ',sx,sy);
    sx++; 
    if(sx==80)
    {
      sx = 0; sy++;
    }
    if(getchxy(sx,sy) != ' ')
    {
      textattr(0xec); gotoxy(sx,sy); cout << "X";
      textattr(0xb0); center(5,"+++ ZAGINIONY W AKCJI +++");
      break;
    }
    if(miasto_zburzone())
    {
      textattr(0xb1); center(5,"*** NOWY AS LOTNICTWA BOMBOWEGO ***");
      break;
    }
  }
}

// Funkcja sprawdza, czy gracz chce kontynuować rozgrywkę
//-------------------------------------------------------
bool dalsza_gra()
{
   char key;
   textattr(0xbc);
   center(7,_pl("Jeśli chcesz zagrać jeszcze raz, naciśnij klawisz [T]."));
   while(!(key = getch())) ;
   return (key == 't') || (key == 'T');   
}     

// Program główny
//---------------
main()
{
  _cinit();
  srand((unsigned)time(NULL)); 
  cursoroff();
  ekran_tytulowy();
  do
  {
    generuj_miasto();
    bombarduj();
  } while(dalsza_gra());
  cursoron();
}
Widok okienka konsoli
w uruchomionym programie
obrazek

       Wyjaśnienie

Gra bombowiec pochodzi z lat 80 ubiegłego wieku. Zasada jest bardzo prosta. Gracz jest pilotem bombowca, który lata ponad bombardowanym miastem (wiem, pomysł niezbyt humanitarny). Naciskając spację gracz zrzuca bombę, która burzy jeden budynek. Dopóki bomba spada, nie można zrzucić następnej bomby. Samolot po każdym przelocie obniża swój lot (prawdopodobnie skończyło mu się paliwo). Jeśli gracz nie zburzy wszystkich budynków, to samolot w końcu uderzy w jeden z nich i gra się zakończy.

Problemem w tej grze jest konstrukcja tzw. pętli zdarzeń (ang. event loop). Pętla ta wykonuje się co 70 milisekund i obsługuje szereg zdarzeń występujących w grze:

Pętla zdarzeń znajduje się w funkcji bombarduj() i jest następująca:

void bombarduj()
{
  int  wynik,sx,sy,bx,by;
  char key;
Na początku funkcji bombarduj deklarujemy kilka zmiennych:
wynik - zwiększana o 1 po zburzeniu każdego segmentu domu
sx,sy - współrzędne samolotu
bx,by - współrzędne bomby
key - naciśnięty klawisz
  wynik = sx = sy = 0;
  bx = by = 24;
Zerujemy wynik oraz sx i sy. Współrzędna samolotu 0,0 odpowiada lewemu górnemu narożnikowi okna konsoli.
Pozycją neutralną dla bomby jest ostatni wiersz okna konsoli, czyli wiersz o numerze 24.
  while(true) // Pętla zdarzeń
  {
Rozpoczynamy pętlę zdarzeń. Jest to pętla nieskończona, która wykonuje się w kółko. Przerwana będzie, gdy samolot uderzy w blok lub gdy całe miasto zostanie zburzone.
    textattr(0xb1); gotoxy(sx,sy); cout << ">";
Na pozycji sx,sy wyświetlamy samolocik.
    if(kbhit())
    {
      while(!(key = getch())) ;
      if((by == 24) && (key == ' '))
      {
        by = sy+1; bx = sx;
      }
    }  
Teraz obsługujemy klawiaturę. Wykorzystujemy funkcję kbhit() z biblioteki newconio. Funkcja ta zwraca true, jeśli został naciśnięty klawisz na klawiaturze. Nie usuwa jednak tego klawisza z bufora klawiatury.

Klawisz odczytujemy w pętli while. Pętla działa dotąd, aż key zawiera kod różny od 0. Jeśli pierwszy odczyt za pomocą getch() zwróci 0, to naciśnięty klawisz jest klawiszem funkcyjnym i należy ponowić odczyt bufora klawiatury, aby odczytać jego kod matrycowy. Do tego celu potrzebujemy właśnie tej pętli while.

Po odczycie klawisza sprawdzamy, czy bomba jest nieaktywna oraz, czy naciśniętym klawiszem jest spacja. Jeśli w obu przypadkach będzie to prawda, ustawiamy pozycję pionową bomby tuż pod samolotem, a pozycję poziomą w tej samej kolumnie co samolot.

    if(by < 24)
    {
      textattr(0xbc); gotoxy(bx,by);
      if(getchxy(bx,by) == ' ')
        cout << '*';
      else
      {
        cout << char(177);
        wynik++;      
      }
    }
Następnie obsługujemy spadek bomby. Jeśli pozycja pionowa bomby nie jest pozycją neutralną 24, to bomba jest w powietrzu. W oknie konsoli na pozycji bx,by umieszczamy znak *, jeśli pozycja ta zawierała wcześniej spację lub znak kratki o kodzie 177, jeśli pozycja zawierała segment domu. W tym drugim przypadku dodatkowo zwiększamy wynik.
    textattr(0x2e); gotoxy(0,24); cout << wynik;
Aktualny wynik wyświetlamy na początku ostatniego wiersza
    delay(70);
Czekamy przez 70 milisekund
    if(by < 24)
    {
      putchxy(' ',bx,by);
      by++;
    }
Jeśli bomba jest w powietrzu, umieszczamy na jej pozycji znak spacji - w ten sposób kasujemy obraz bomby. Zwiększamy jej pionową pozycję - w następnym obiegu pętli bomba będzie wyświetlona o wiersz niżej.
    putchxy(' ',sx,sy);
    sx++; 
    if(sx==80)
    {
      sx = 0; sy++;
    }
Kasujemy obraz samolotu. Zwiększamy o 1 pozycję w poziomie sx. Jeśli samolot wyleciał poza prawą krawędź okna, pozycję sx zerujemy i zwiększamy o 1 pozycję w pionie sy. Spowoduje to w następnym obiegu pętli pojawienie się samolotu z lewej strony okienka konsoli, ale w wierszu niższym - samolocik opada.
    if(getchxy(sx,sy) != ' ')
    {
      textattr(0xec); gotoxy(sx,sy); cout << "X";
      textattr(0xb0);
      center(5,"+++ ZAGINIONY W AKCJI +++");
      break;
    }
Sprawdzamy, czy na nowej pozycji samolocik nie uderzył w budynek. Jeśli tak, na pozycji samolotu wypisujemy literkę X oraz odpowiedni tekst. Pętla zdarzeń jest przerywana.
    if(miasto_zburzone())
    {
      textattr(0xb1);
      center(5,"*** NOWY AS LOTNICTWA BOMBOWEGO ***");
      break;
    }
  }
}
Sprawdzamy, czy miasto zostało zburzone. Jeśli tak, wypisujemy odpowiedni komunikat i przerywamy pętlę zdarzeń.

Pozostała część programu nie powinna sprawić większych kłopotów.

Zastanów się, jak w pętli zdarzeń można dodać dodatkową opcję - naciśnięcie klawisza ESC (kod 27) zawsze kończy bieżącą rozgrywkę, czyli przerywa wykonywanie pętli zdarzeń.


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2024 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe