Monday, 16 November 2015

Image source: (cgcookie.com/2DSpaceShooter)

U ovom izdanju AllegroLab-a II pogledat ćemo na implementaciju i razvoj primitivne while petlje u programu kojim se sprite pomjera tipkovnicom u različitim pozicijama na zaslonu. Ovaj AllegroLabII je dio moje Allegro2D radionice u kojoj se izrađuje moja prva interaktivna 2D CometShooter igrica. Da ne dužim bespotrebno, pređimo na posao.

Izvorni tehnički članak,
Piše: Amar Tufo

Opis zadatka:

U ovom labu naš zadatak je da modifikujemo primjer programa Allegro_Bitmap tako da u izvornom kodu kreiramo timer event handler koji će nam omogučiti da se naš program izvršava svakih 60 sekundi na screen-u. Šta to znači? To znači da se objekti igrice, spritovi, animacije, slike, i fontovi ažuriraju i iscrtavaju svakih 60 puta u jednoj sekundi. Kako je to moguće postiči? Vrlo jednostavno! Sve što treba da uradimo jeste kreiranje FPS funkcije ili frames per second. Teoretski govoreči, naša igrica kada se jednom pokrene, korisnik će imati istu brzinu gameplay-a bez obzira na brzinu računala. Ovako u ovom tekstu, ukoliko nastavim dalje, mogao bih vas zbuniti. Pa da ne duljim, pogledajmo kako izgleda while petlja bez timer-a. Ali prije nego nastavimo, treba da znamo šta je to Allegro_Timer?

Allegro_Timer?

Dok igrate neke zahtjevne video igre, podrazumjeva se da vam je HD grafička karta i da je vaš računar barem 2 do 4 GB RAM-a jak. To znači da ćete za ove performanse dobiti podjednaku brzinu gameplay-a vaše igre i da ćete biti u stanju primjetiti velike razlike u FPS-u koji vaša igrica koristi. No, kada dođe do 2D video igara, jako je neophodno da takve igrice imaju svoj FPS jer će se večinom pokretati na slabijim računalima, to se i podrazumjeva. U programiranju video igre koristeči se Allegro bibliotekom unutar koje postoji funkcija Allegro_Timer koja developeru omogučava da vidi brzinu svoje igrice koju izrađuje i igra, a u isto vrijeme mu omogučava da igračima svoje igre dostavi brzinu od recimo 60 FPS-ova u sekundi bez obzira na slabe performanse test računala.

Ovaj lab je dio moje Allegro2D game dev serije pa ja stoga neću govoriti u detalje kako i na koji način kreirati cio projekat, jer podrazumjevam da ste ranije radili u C++-u ili u Visual C++ 2008 razvojnom okruženju i da ste već dosada napravili neke svoje aplikacije makar to bio i HelloWorld ili desktop aplikacija koja pritisko na taster pozdravlja korisnika nekom porukom. Ono što ću iznjeti u daljem djelu ovog članka jeste while petlja kao glavni dio ovog laba. Vidjet ćemo dvije while petlje i ukazati na njihove razlike kako bismo rješili naš problem sa pomjeranjem sprajta na zaslonu. Primjer veoma jednostavne while petlje je dat dolje ispod ovog teksta.


//main loop
 while(!exit)
 {
  ALLEGRO_EVENT ev;
  al_wait_for_event(event_queue, &ev);

  if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
  {
   switch(ev.keyboard.keycode)
   {
   case ALLEGRO_KEY_ESCAPE:
    exit = true;
    break;
   }
  
  } 
U ovoj klasičnoj while petlji nema ništa tako bitno što je vrijedno pomena. Tu se, doduše, nalazi jedna swithc case funkcija koja zatvara aplikacije pritiskom tastera escape na tastaturi. While petlja ili game petlja je jako važan dio bilo koje igrice jer se u njoj provjerava unos s tastature, kontrole, razni drugi djelovi igrice kao što je rendering, animacije i još niz drugih stvari. A sada pogledajmo kako bi izgledala unapređena while petlja na primjeru iz opisa ovog teksta. Da se posjetimo, sprite mora da se pomjera jako glatko na zaslonu a ne step- by -step. Da bismo to postigli u tu svrhu je deklariran timer ili jednostavno FPS regulator. 

Prije nego li pogledamo kako cijela while petlja izgleda, pogledajmo izvorni kod Allegro_Bitmap programa kojeg treba da modifikujemo.  

//Allegro_Bitmap_App
//Developer: Amar Tufo
//8. October, 2015
//This sample demonstrates the use of bitmaps in Allegro
//and way of manipulating the bitmaps in actual programs
 
#include <allegro5\allegro.h>
#include <allegro5\allegro_primitives.h>
#include <allegro5\allegro_image.h>


int main()
{
//variables
 int width = 640;
 int height = 480;
 bool exit = false;

 int imageWidth = 0;
 int imageHeight = 0;

 int x = 0;
 int y = 0;
 
 //allegro variable
 ALLEGRO_DISPLAY *display = NULL;
 ALLEGRO_EVENT_QUEUE *event_queue = NULL;
 ALLEGRO_BITMAP *image = NULL;

 //program init
 if(!al_init())          //initialize Allegro
  return -1;

 display = al_create_display(width, height);   //create our display object

 if(!display)          //test display object
  return -1;

 //addon init
 al_install_keyboard();
 al_init_image_addon();

 image = al_load_bitmap("slika.png"); //local folder

 imageWidth = al_get_bitmap_width(image);
 imageHeight = al_get_bitmap_height(image);

 x = width / 2 - imageWidth / 2;
 y = height / 2 - imageHeight / 2;
       
        //installing event sources    
 event_queue = al_create_event_queue();
 al_register_event_source(event_queue, al_get_keyboard_event_source());
        
        //main loop starts here
 while(!exit)
 {
  ALLEGRO_EVENT ev;
  al_wait_for_event(event_queue, &ev);

  if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
  {
   switch(ev.keyboard.keycode)
   { //allow user to exit the program
   case ALLEGRO_KEY_ESCAPE:
    done = true;
    break;
   case ALLEGRO_KEY_UP:
    y -= 10; 
    break;
   case ALLEGRO_KEY_DOWN:
    y += 10;
    break;
   case ALLEGRO_KEY_LEFT:
    x -= 10;
    break;
   case ALLEGRO_KEY_RIGHT:
    x += 10;
    break;
   }
  } //main loop ends here

  al_draw_bitmap(image, width / 2 - imageWidth / 2, height / 2 - imageHeight / 2, 0);
  al_draw_tinted_bitmap(image, al_map_rgba(255, 0, 0, .2),  x,  y, 0);

  al_flip_display();
  al_clear_to_color(al_map_rgb(0,0,0));
 }

 al_destroy_bitmap(image);
 al_destroy_event_queue(event_queue);
 al_destroy_display(display);      //destroy our display object

 return 0;
} 
Dakle, ovo je izvorni kod Allegro_Bitmap programa u kojem treba da napravimo modifikaciju tako da se sprajt na zaslonu pomjera koristeči FPS timer a ne step by step ili piksel po piksel. Ovako teoretski ne možete vidjeti šta to znači pa sam stoga pripremio i kratki video demo ovog programa u akciji kako biste vidjeli što je to FPS. 


Demonstracija pomjeranja sprajta na zaslonu bez upotrebe FPS-a

Glavna razlika u gornjem izvornom kodu jeste ta što program nema deklariran FPS event handler, odnosno da se program ne izvršava 'glatko' kako bi trebao. Da bi se ovo rješilo potrebno je deklarirati nekoliko manjih varijabli koje omogučavaju programu da se svi njegovi elementi pokreću ili iscrtavaju svakih 60 puta u jednoj sekundi. Dakle, FPS je po standartu jednak 60 sekundi pa stoga program koristi istu brzinu rada bez obzira na performanse vašeg računala na kojemu se program izvršava. 

Modifikacije koje treba da napravimo u gornjem izvornom kodu su sljedeće. Prvo trebamo da modifikujemo pomjeranje sprajta na zaslonu u samom izvornom kodu. Ne želimo nikako da nam se sprajt pomjera svakih 10 piksela ili korak po korak. Naš cilj je da se sprajt pomjera onoliko dugo po zaslonu koliko mi držimo određene tastere (strelice) naše tastature, tek onda kada otpustimo naše strelice sprajt će prestati sa pomjeranjem. Kako sam već i rekao, prvu stvar koju radimo jeste deklaracija FPS varijable (int FPS = 60;). Ova varijabla je kreirala FPS od 60 sekundi. Sada treba da deklariramo poseban pokazivać na timer. Srećom, Allegro u sebi ima timer handler koji nam omogučava da ostvarimo željeni efekat uz jako malo koda. Evo i kako.

Timer će nam omogučiti adekvatnu primjenu FPS varijable u našem kodu pa bi se naš sprajt trebao brzo pomjerati na zaslonu bez obzira na performanse računala. Deklaracija timer-a je sljedeća:  ALLEGRO_TIMER *timer = NULL; Ovde kako vidimo imamo pokazivać po imenu timer na varijablu Allegro_Timer. Pokazivači su jako bitan segment u programiranju bilo video igara pa tako i drugih aplikacija pa je njihovo razumjevanje od prijeke važnosti. Nakon deklaracije Allegro_Timer varijable trebali bismo registrovati izvor za ovu varijablu a to radimo sljedećom linijom koda: al_register_event_source(event_queue, al_get_timer_event_source(timer)); Ovom linijom koda smo kazali Allegru koji izvor akcije koristimo u doslovnom smislu. Nakon kreiranja i potvrde korištenja Timer varijable potrebno je da uključimo timer ovom linijom koda: al_start_timer(timer)
 
Glavni dio ovog koda jeste naša sređena while petlja koju ovdje prenosim u cjelosti uz popratne komentare pa ću komentirati u nastavku while petlju za pojedine nejasnoće. 
 
//while loop start's here
  while(!exit)
 {
  //creating allegro event
  ALLEGRO_EVENT ev;
  al_wait_for_event(event_queue, &ev);
  
  //if keyboard key is pressed then
  if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
  {
   switch(ev.keyboard.keycode)
   {
   case ALLEGRO_KEY_UP:
    keys[UP] = true;
 break;
   case ALLEGRO_KEY_DOWN:
  keys[DOWN] = true;
    break;
   case ALLEGRO_KEY_RIGHT:
   keys[RIGHT] = true;
    break;
   case ALLEGRO_KEY_LEFT:
    keys[LEFT] = true;
    break;
   }
  }
  //keep track of my keys
 else if(ev.type == ALLEGRO_EVENT_KEY_UP)
  {
   switch(ev.keyboard.keycode)
   {
   case ALLEGRO_KEY_UP:
    keys[UP] = false;
 break;
   case ALLEGRO_KEY_DOWN:
  keys[DOWN] = false;
    break;
   case ALLEGRO_KEY_RIGHT:
   keys[RIGHT] = false;
    break;
   case ALLEGRO_KEY_LEFT:
    keys[LEFT] = false;
    break; //if user press escape key
   case ALLEGRO_KEY_ESCAPE:
    exit = true; //than exit
    break; //if user press space key
   case ALLEGRO_KEY_SPACE:
    exit = true;//than exit
    break;
   }
  } 
 
  //closing window by pressing red X
  else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
  { //while loop end's here
   exit = true; 
  } //using allegro_timer event handler
  else if(ev.type == ALLEGRO_EVENT_TIMER)
  {
    //move my square every 10 pixels
    y -= keys[UP] * 10;
    y += keys[DOWN] * 10;
    x -= keys[LEFT] * 10;
    x += keys[RIGHT] * 10;
   
  } 
Listing1: Primjer while petlje sa upotrebom FPS i Timer varijable

Ja sam večinu ovih linija koda prethodno iskomentarisao kako bi se snašao u budućim modifikacijama istog izvornog koda ukoliko bude bilo potrebe za njih. Nešto posebno da kažem nemam, ali se ovaj dio koda bitno razlikuje od onog gore koji je prikazan u ovom članku jer ovaj kod ima TIMER event handler koji sada automatski startuje program bez potrebe da se ćeka na određeni pritisak tastera na tastaturi kako bi se program pokrenuo. Osim toga, kako ćete u nastavku imati priliku da vidite, program se izvršava relativno brzo gdje koristi svoje resurse za rad pa se sprajt dosta brže pomjera na zaslonu. Cio izvorni kod konačnog rješenja ovog laba možete pogledati u nastavku.

 
//Allegro_Bitmap_App
//Developer: Amar Tufo
//12. October, 2015
//This sample demonstrates the use of bitmaps in Allegro
//and way of manipulating the bitmaps in actual programs
//by using FPS
 
#include <allegro5\allegro.h>
#include <allegro5\allegro_primitives.h>
#include <allegro5\allegro_image.h>

enum KEYS{UP, DOWN, LEFT, RIGHT};

int main()
{
 int width = 700;
 int height = 650;
 bool exit = false;
 bool keys[4] = {false, false, false, false};

 int imageWidth = 0;
 int imageHeight = 0;
 int x = 0;
 int y = 0;
 //int pos_x = width / 2;
 //int pos_y = height / 2;

 //creating speed regulation
 int FPS = 60;

  //float scale = 2;

 //float degree = 0;

 //Creating allegro variables
 ALLEGRO_DISPLAY *display = NULL;
 ALLEGRO_EVENT_QUEUE *event_queue = NULL;

 ALLEGRO_BITMAP *image = NULL;
 
 //Creating timer event handler
 ALLEGRO_TIMER *timer = NULL;


 //allegro init check
 if(!al_init())
  return -1;

 display = al_create_display(width, height);

 //check display initialisation
 if(!display)
  return -0;

 //addon init
 al_install_keyboard();
 al_init_primitives_addon();
 al_init_image_addon();

 //register timer event
 timer = al_create_timer(1.0 / FPS);

 //load image into memory 
 image = al_load_bitmap("slika.png");
 
 //determine width & height of image
 imageWidth = al_get_bitmap_width(image);
 imageHeight = al_get_bitmap_height(image);

 //initialising variables
 x = width / 2 - imageWidth / 2;
 y = height / 2 - imageHeight / 2;

 //registering event sources
 event_queue = al_create_event_queue();
 al_register_event_source(event_queue, al_get_keyboard_event_source());
 al_register_event_source(event_queue, al_get_timer_event_source(timer));
 al_register_event_source(event_queue, al_get_display_event_source(display));


 //start timer
  al_start_timer(timer);

  //while loop start's here
  while(!exit)
 {
  //creating allegro event
  ALLEGRO_EVENT ev;
  al_wait_for_event(event_queue, &ev);
  
  //if keyboard key is pressed then
  if(ev.type == ALLEGRO_EVENT_KEY_DOWN)
  {
   switch(ev.keyboard.keycode)
   {
   case ALLEGRO_KEY_UP:
    keys[UP] = true;
 break;
   case ALLEGRO_KEY_DOWN:
  keys[DOWN] = true;
    break;
   case ALLEGRO_KEY_RIGHT:
   keys[RIGHT] = true;
    break;
   case ALLEGRO_KEY_LEFT:
    keys[LEFT] = true;
    break;
   }
  }
  //keep track of my keys
 else if(ev.type == ALLEGRO_EVENT_KEY_UP)
  {
   switch(ev.keyboard.keycode)
   {
   case ALLEGRO_KEY_UP:
    keys[UP] = false;
 break;
   case ALLEGRO_KEY_DOWN:
  keys[DOWN] = false;
    break;
   case ALLEGRO_KEY_RIGHT:
   keys[RIGHT] = false;
    break;
   case ALLEGRO_KEY_LEFT:
    keys[LEFT] = false;
    break; //if user press escape key
   case ALLEGRO_KEY_ESCAPE:
    exit = true; //than exit
    break; //if user press space key
   case ALLEGRO_KEY_SPACE:
    exit = true;//than exit
    break;
   }
  } 
 
  //closing window by pressing red X
  else if(ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE)
  { //while loop end's here
   exit = true; 
  } //using allegro_timer event handler
  else if(ev.type == ALLEGRO_EVENT_TIMER)
  {
    //move my square every 10 pixels
    y -= keys[UP] * 10;
    y += keys[DOWN] * 10;
    x -= keys[LEFT] * 10;
    x += keys[RIGHT] * 10;
   
  }
  //draw on the screen if there are no other event to process
    al_draw_bitmap(image, width / 2 - imageWidth / 2, height / 2 - imageHeight / 2, 0);
    //drawing tinted image on screen
   al_draw_tinted_bitmap(image, al_map_rgba(125,100,100, .5), x, y, 0);
  //draw scaled image on the screen
  //al_draw_scaled_bitmap(image, 0, 0, imageWidth, imageHeight, x, y, imageWidth * scale, imageHeight * scale, 0);

  //draw rotated image to the center of the screen
  //al_draw_rotated_bitmap(image, imageWidth / 2, imageHeight / 2, width / 2, height / 2, degree * 3.14159 / 180, 0);
  
  //draw image on center of the screen
  //al_draw_bitmap(image, width / 2 - imageWidth / 2, height / 2 - imageHeight / 2, 0);

  al_flip_display();
  al_clear_to_color(al_map_rgb(0,0,0));

  }
  al_destroy_bitmap(image);
  al_destroy_event_queue(event_queue);
  al_destroy_display(display);

  return 0;
} 
Konačno rješenje Allegro_Lab-: Moving image by FPS and Timer on the screen (Video)

Zaključak:

Bez obzira da li pravili 2D ili 3D igricu veoma je važno da vaša igrica obezbjedi takvu igraču atmosferu gdje greškama i minusima u brzini izvršavanja vaše igre jednostavno mjesta nema. Obzirom da tehnologije svaki dan napreduju, Allegro je i dalje jedan od onih izvrsnih API-a koji nam omogučavaju izradu visoko kvalitetnih i interaktivnih 2D/3D igara koje su kompatibilne sa skoro svim vodećim platformama. Jednom razvijena u Windows okruženju, vaša Allegro igrica će se bez problema pokrenuti na Mac-u ili Ubuntu Linux-u. Stoga je poliranje brzine i upotreba FPS-a svakako prijeko potrebna funkcija koja vašoj igri daje onu dozu adrenalina koja drži korisnika prikovanog uz tastaturu i monitor bez obzira o kakvoj vrsti i tipu igre je rjeć. S druge strane, FPS nije luksuz u video igricama barem koliko sam ja upoznat, nego je on jedan njen sastavni dio koji omogučava nesmetano igranje vaše igrice bez obzira koji OS i brzinu vaše računalo koristi. Zato u početku, razvoj igara treba da bude fokusiran isključivo samo na 2D video igrice kojima ne samo da ćete izgraditi temelj za prijelaz u 3D svijet game developmenta, nego je to jedan od vjerovatno najboljih načina da savladate programiranje u suštini i ovladate C++ programskim jezikom. 

__________________________
© Amar Tufo, 2015
www.amartufointerctive.org 
18. Novembar, 2015

Reakcije:

0 komentari:

Post a Comment

Weather

Advertisement

Follow me on Facebook:

Popular Posts