[Grafika] [WebTip] [Fotografování] [Galerie] [MujMac] [Printing]
  Redakce: info (at) builder.cz   Inzerce: reklama (at) grafika.cz
Diskuzní fóra
.Net (68468)
ASP (1591)
ActiveX (168)
Allegro (136)
Assembler (3926)
C++ Builder (23160)
C/C++ (44499)
Databáze (30680)
Delphi (78806)
DelphiX (1655)
DirectX (1464)
Java (39508)
JavaScript (12598)
Matematické programy (2178)
OOP a UML (732)
OpenGL (6920)
Php (65224)
PowerBuilder (464)
Problémy a algoritmy (10473)
Programování v Linuxu (2000)
Právo a programování (3384)
Python (1353)
Ruby (136)
Visual Basic (12078)
Visual C++ (12956)
Wap (56)
Web (10895)
Web servery (5549)
Win32 (13553)
Windows CE (865)
XML/XSL (1860)
Textová inzerce
Služby Builder.cz
  • Bazar - koupím(0)
  • Bazar - prodám(0)
  • Hledám práci(0)
  • Nabízíme práci(0)
  • Projekty(0)
  • Allegro - Jednoduchá hra Střelba (6. díl)
    Dnešní článek nás už konečně seznámí s nějakou hrou, kterou jsme schopni v Allegru vytvořit. Je to opravdu jen to zakladní a později se vrhneme na opravdovou hru!
    Allegro - Knihovna pro programování her
    Předchozí díl: Allegro - Myš a klávesnice (5. díl)

    Následující díl: Allegro - Instalace a konfigurace (7. díl)
    Autor: Matoušek Pavel
    Rubrika: C/C++
    Publikováno: 30.01. 2002
     Tisk článku
    Poslat odkaz emailem
     

    Na úvod

    Než začneme něco tvořit povíme si ve zkratce to základní o programování her. Jak by měl asi tak vypadat kód nebo spíše hlavní smyčka programu. Je opravdu hodně možností, ale pro začátek si povíme o tom nejjednodušším. Především je tento seriál o knihovně pro programování her a tudíž budeme postupně tvořit lepší a lepší hry a ne např. databázové programy ;-).

    Něco málo o programovaní her

    Hlavní funkce main() by měla obsahovat inicializaci hry, hlavní smyčku (tedy vstup, přepočet scény a zobrazení na display) a odinicializaci hry nebo spíše uvolnění paměti zabrané při inicializaci.

    Poznámka: Asi jste si všimli, že používám převážně C a C++ jen okrajově (např. deklarace použitím, implicitní hodnoty u funkcí atd.). Tedy nepoužívám klíčové slovo class (vlastně objekty), ale k tomu se s postupem času taky dostanu, ale na tak malé ukázky se to nevyplatí.

    Ukázka takového zdrojového kódu v C/C++.
    #include "allegro.h"
    
    int Konec = 0;
    
    int main(void)
    {
    	GameInit();
    
    	while (!Konec)
    	{
    		Input();
    		Tick();
    		Render();
    	}
    
    	GameUnInit();
    
    	return 0;
    }
    END_OF_MAIN();

    Jak vidíte nejedná se o nic těžkého. Jednotlivé funkce je potřeba napsat podle vlastního uvážení a především podle toho o co se to jedná za hru.

    Jednoduchá hra - Střelba

    Nejjednodušší bude si všechno ukázat na plně funkčním programu. Naprogramoval jsem jednoduchou hru kde máte za úkol sestřelit kámen na obrazovce, který po sestřelení opět vyletí, ale z jiné strany. Dohromady 10x sestřelíte kámen a vyhráli jste a pokud se tak nestane a kámen Vám uletí z obrazovky přicházíte o život. Dohromady jich máte 5.

    Celý program je ke stažení, ale já se budu zde  zabývat jednotlivýma funkcemi, aby všichni věděli o co vlastně jde.

    První funkce, kterou již známe není třeba představovat je totiž popsána v dílu č.4 a jmenuje se FlipToScreen();

    Další pomocnou funkcí je:

    void Chyba(const char *str)
    {
    	allegro_exit();
    	printf("%s");
    	exit(0);
    }

    Ta nám okamžitě ukončí program s chybovým hlášením. Používám ji při testování grafiky a inicializaci hry.

    Proměnné
    Nyní na to jaké používám ve hře globální proměnné.

    BITMAP *BackBuffer;
    BITMAP *Pozadi;
    BITMAP *MouseSprite;
    BITMAP *KoleckoSprite;

    Tyto v sobě uchovávají - BackBuffer pro Double Buffering, pozadí scény, kurzor myši a kámen nebo kolečko.

    int Konec = 0;
    int PocetKolecek = 10, Uletlo = 0;
    int i = 1, Vypis = 0, VypisStisk;

    Dále Konec testuje hlavní smyčku, PocetKolecek a Uletlo složí k počínání sestřelení nebo ulétnutí kamenů a i používám kontrolu špatně vypočítaného nového kolečka. Vypis a VypisStisk slouží ke kontrole výpisu k tomu se dostanu později.

    Na vlastnosti o Kolečku ( kámen a kolečko je jedno a to samé ) používám tuto strukturu:

    typedef struct tagKolecko {
    int x;
    int y;
    int Speed;
    double Smer;
    } Kolecko;

    Kolecko Kolo;

    Kde x,y jsou souřadnice na obrazovce. Speed je rychlost kolečka a Smer je úhél otočení podle jednotkové kružnice.
    Kolo je proměnná typu Kolečko, které lítá po obrazovce.

    Nyní se ještě podívejme na další pomocné funkce:

    void KresliKolecko(Kolecko k)
    
    {
      masked_blit(KoleckoSprite, BackBuffer, 0, 0, k.x, k.y, KoleckoSprite->w, 
                   KoleckoSprite->h);
    }

    Tato funkce dostane jako parametr naše kolečko a vykreslí jej na obrazovku na zvolené souřadnice.

    void NewKolecko(Kolecko *k)
    {
    	int Zacatek;
    
    	Zacatek = rand() % 4;
    
    	switch(Zacatek)
    	{
    		case 0: k->x=0;
    				k->y=240;
    				break;
    		case 1: k->x=640;
    				k->y=240;
    				break;
    		case 2: k->x=320;
    				k->y=0;
    				break;
    		case 3: k->x=320;
    				k->y=480;
    				break;
    	}
    	k->Smer = rand() % 360;
    	k->Speed = rand() % 5 + 5;
    }

    a tato funkce nám pro změnu vygeneruje nové kolečko. Příkaz switch nám zjišťuje odkud nové kolečko vyletí. Jsou 4 možnosti a to z každé strany obrazovky přesně uprostřed.

    A nyní se dostáváme k jednotlivým funkcím o kterých jsem se zmínil na začátku článku. Probereme si je hezky popořadě.

    void GameInit(void)
    {
    	allegro_init();
    	install_keyboard();
    	install_mouse();
    	
    	set_color_depth(16);
    	int Result = set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0);
    	if (Result < 0)
    		Chyba("Grafika");
    
    	BackBuffer = create_bitmap(640,480);
    
    	if (BackBuffer == NULL)
    		Chyba("BackBuffer");
    
    	Pozadi = load_bitmap("./data/mraky.bmp", NULL);
    
    	if (Pozadi == NULL)
    		Chyba("Pozadi");
    
    	KoleckoSprite= create_bitmap(25, 25);
    	if (KoleckoSprite == NULL)
    		Chyba("KOLECKO SPRITE");
    	clear_to_color(KoleckoSprite, makecol(255, 0, 255));
    	circlefill(KoleckoSprite, 12, 12, 10, makecol(0, 0, 0));
    	for(int i=0;i<11;i++)
    		circle(KoleckoSprite, 12, 12, i+1, makecol(32*i, 32*i, 0));
    
    	MouseSprite = load_bitmap("./data/mousesprite.bmp", NULL);
    	if (MouseSprite == NULL)
    		Chyba("MOUSE SPRITE");
    	set_mouse_sprite(MouseSprite);
    	set_mouse_sprite_focus(9, 8);
    
    	srand( (unsigned)time( NULL ) );
    	NewKolecko(&Kolo);
    }

    Tato funkce nám zapne Allegro včetně myši a klávesnice. Nastaví graf. mód 640*480*16. Vytvoří BackBuffer, Pozadí do kterého nahraje soubor mraky.bmp z adresáře Windows. Další je inicializace kolečka a for cyklus nám do něj nakreslí několik koleček různých barev a nakonec nahrání a nastavení kurzoru myši.

    Po inicializaci ve funkci main(); se dostáváme do hlavní smyčky programu kde jako první voláme funkci:

    void Input(void)
    {
    	if (key[KEY_ESC])
    	{
    		Konec = 1;
    		return;
    	}
    
    	if (mouse_b & 1)
    	{
    		if (Kolo.x < mouse_x && Kolo.x+24 > mouse_x && 
    			Kolo.y < mouse_y && Kolo.y+24 > mouse_y)
    		{
    			Kolo.Speed = -1;
    			PocetKolecek--;
    			i=1;
    		}
    	}
    	if (key[KEY_V] && VypisStisk)
    	{
    		Vypis = !Vypis;
    		VypisStisk = 0;
    	}
    
    	if (!key[KEY_V])
    		VypisStisk = 1;
    	
    }

    Ta se nám stará o všechny vstupy od myši a klávesnice ( tedy ne o všechny, ale jen o ty co je potřeba ). Klávesa V nám zapíná a vypíná pomocné informace, ESC klávesa ukončí program a zmáčknutím levého tlačítka myši sestřelíme kolečko na obrazovce, pokud jsme na něj klikni. Pokud klikneme mimo nic se neděje. i používám na kontrolu ve funkci tick k tomu se tedy dostaneme až pak. Speed nastavím na -1, abych věděl, že kolečko bylo sestřeleno.

    Další funkce je tick, která se stará o přepočítání scény inicializaci nového kolečka apod.

    void Tick(void)
    {
    znovu:
    	sx = cos((double)(((360-Kolo.Smer)/360) * 2*PI)) * (double) Kolo.Speed;
    	sy = sin((double)(((360-Kolo.Smer)/360) * 2*PI)) * (double) Kolo.Speed;
    
    	Kolo.x += (int)sx;
    	Kolo.y += (int)sy;
    
    
    	if (Kolo.x > 640 || Kolo.x < 0 || Kolo.y > 480 || Kolo.y < 0 || 
                Kolo.Speed == -1)
    	{
    		if (i==0)
    		{
    			Uletlo++;
    			i=1;
    		}
    		NewKolecko(&Kolo);
    		goto znovu;
    	}
    	i = 0;
    
    	if (PocetKolecek <=0 || Uletlo >5)
    	{
               clear(BackBuffer);
               textprintf_centre(BackBuffer, font, 320, 240, makecol(255, 0, 0), 
                                  "KONEC");
               if (Uletlo > 5)
                textprintf_centre(BackBuffer, font, 320, 250, makecol(255, 0, 0),
                                  "Prohral JSI!");
                else
                textprintf_centre(BackBuffer, font, 320, 250, makecol(255, 0, 0), 
                                  "Vyhral JSI!");
                Konec = 1;
                FlipToScreen();
                clear_keybuf();
                readkey();
                GameUnInit();
                exit(0);
    	}
    
    }

    Pomocí sin a cos vypočítá posunutí kolečka, které uběhlo za jeden frame (frame je překreslení scény a fps - frame per second je počet překreslení obrazovky za jednu vteřinu u této hry je omezovač na 50 FPS - k tomu se ale ještě dostanu).

    Dále překontroluji jestli není kolečko mimo obrazovku a jestli je nebo není sestřeleno. Pokud je vše OK tak vygeneruji nové kolečko a opět to zkontroluji. Proto je tak příkaz goto, i když ho používám nerad, ale v tomto případě je i efektivní. 

    Nakonec zjistím zda není konec hry. To znamená, že hráč buď sestřelil všech 10 koleček a nebo mu jich 5 ulétlo :-).

    Poslední (ne úplně poslední, ale poslední v cyklu) je funkce Render(),  která se postará o všechno vykreslení.

    void Render(void)
    {
     show_mouse(NULL);
     clear(BackBuffer);
     blit(Pozadi, BackBuffer, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
     
     if (Vypis == 1)
      {
       text_mode(-1);
       textprintf(BackBuffer, font, 10, 10, makecol(255, 0, 0), "X = %d", Kolo.x);
       textprintf(BackBuffer, font, 10, 30, makecol(255, 0, 0), "Y = %d", Kolo.y);
       textprintf(BackBuffer, font, 10, 50, makecol(255, 0, 0),
                  "Speed = %d", Kolo.Speed);
       textprintf(BackBuffer, font, 10, 70, makecol(255, 0, 0),
                  "Smer = %f", Kolo.Smer);
    
       textprintf(BackBuffer, font, 150, 60, makecol(255, 0, 0), "SX = %f", sx);
       textprintf(BackBuffer, font, 150, 70, makecol(255, 0, 0), "SY = %f", sy);
    
       textprintf(BackBuffer, font, 350, 70, makecol(255, 0, 0), "X = %d", mouse_x);
       textprintf(BackBuffer, font, 350, 80, makecol(255, 0, 0), "Y = %d", mouse_y);
       textprintf(BackBuffer, font, 350, 90, makecol(255, 0, 0), "B = %d", mouse_b);
    
       textprintf(BackBuffer, font, 350, 120, makecol(255, 0, 0), 
                   "PocetKolecek = %d", PocetKolecek);
       textprintf(BackBuffer, font, 350, 130, makecol(255, 0, 0), "Uletlo = %d",
                              Uletlo);
     }
    
     KresliKolecko(Kolo);
     show_mouse(BackBuffer);
     FlipToScreen();	
    }

    Nejdříve schová kurzor, poté smaže obrazovku, nahraje pozadí, vykreslí pomocné informace pokud jsou aktivovány klávesou V, nakreslí kolečko pomocí funkce o které již byla řeč, zobrazí na BackBuffer kurzor myší a nakonec vše plácne na obrazovku.

    Po opuštění cyklu se zavolá funkce GameUnInit, která se postará o odinicializaci hry. Není zapotřebí více komentovat.

    void GameUnInit(void)
    {
    	destroy_bitmap(BackBuffer);
    	destroy_bitmap(Pozadi);
    	destroy_bitmap(MouseSprite);
    	destroy_bitmap(KoleckoSprite);
    }

    A nyní se podíváme na hlavní funkci main().

    Vypadá jako jsem uváděl předtím až na to, že je zde navíc funkce rest(), která je stejná jako delay(int ms); u jazyka C.

    int main(void)
    {
    	GameInit();
    
    	while (!Konec)
    	{
    		Input();
    		Tick();
    		Render();
    
    		rest(20);
    	}
    
    	GameUnInit();
    
    	return 0;
    }
    END_OF_MAIN();

    Tedy počítáme s tím, že hra poběží v 50 FPS, což je docela pěkné, ale je zapotřebí výkonný počítač. Při další hře si povíme o synchonizaci FPS na jakémkoliv stroji. Aby tedy bylo jedno zda hra beží 10 nebo 100 FPS a kámen se posouval o stejný kus. Při 10 FPS to může mít za následek tzv. trhání.

    Zdrojový příklad si můžete stáhnout zde.


    Zpět na začátek stránky

    Pavel Matoušek
    Osobní stránky autora naleznete na www.pmatousek.com

    Hodnocení článku
    1 | 2 | 3 | 4 | 5
    Aktuální známka: 2.48
    (Počet známek: 3229)

    Komentáře k článku
    Pavel Matoušek - Autor04.02.15:43Instalace a konfigurace již příští díl č. 7!!!
    PETR02.02.10:29FONTY
    Pavel Matoušek - Autor02.02.13:52RE: FONTY
    Pavel Matoušek31.01.22:41Dodatek autora článku!
    mol31.01.21:54tip
    Pavel Matoušek31.01.22:28RE: tip
         





    info@builder.cz
    Vydává Grafika Publishing, s.r.o.
    Copyright (c) 1997-2002 Všechna práva vyhrazena