Allegro - Jednoduchá hra Střelba (6. díl) - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Soutěž

Sponzorem soutěže je:

IDIF

 

Kde se koná výstava fotografií Luďka Vojtěchovského?

V dnešní soutěži hrajeme o:



C/C++

Allegro - Jednoduchá hra Střelba (6. díl)

30. ledna 2002, 00.00 | 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!

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.

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » C/C++  

Poslat článek

Nyní máte možnost poslat odkaz článku svým přátelům:

Váš e-mail:

(Není povinný)

E-mail adresáta:

Odkaz článku:

Vzkaz:

Kontrola:

Do spodního pole opište z obrázku 5 znaků:

Kód pro ověření

 

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: