Allegro - Vektory a synchronizace scény (11. 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:



C/C++

Allegro - Vektory a synchronizace scény (11. díl)

21. března 2002, 00.00 | Dnešní díl nás seznámí s vektory jak z matematiky tak z knihovny STL v C++. Povíme si o synchonizaci scény na jakém koliv počítači a vše si ukážeme na pěkném demu.

Pro dnešní díl je potřeba se trochu seznámit s vektory. Opravdu to nebude nic těžkého a každý by to měl pochopit pokud ještě neví co to vektory jsou. Nebudu přímo vysvětlovat matematické vektory, abych Vás zbytečně nezatěžoval, ale seznámím Vás s principem - jak udělat, aby se mi obrázek na obrazovce posouval o stejný kus a nezáleželo na tom jak rychlý mám počítač?

Nejlepší je vše si vysvětlit opět na příkladu. Vytvořil jsem jednoduché demo, které vidíte na obrázku.

Pohybují se na obrazovce čtverečky a odrážejí se od stěn. Program taky ukazuje počet čtverečků na obrazovce a FPS.

Ovládání

SPACE - přidá jeden čtvereček
ENTER - odebere jeden čtvereček
INSERT - přidá sto čtverečků
DELETE - smaže všechny čtevečky
ESC - konec 

Doporučuji si nejdříve stáhnout ukázkový příklad zde, pustit si ho a malinko mrknout na zdroják, bude se vám pak dnešní článek lépe chápat. Nezapomeňte si správně nastavit v programu cestu k obrázku. To by se Vám objevila jenom černá obrazovka.

Jak je to řešeno programově?

Na souřadnice jednotlivých čtverečků používám objekty ( class z C++, už o tom byla zmínka, že to jednou přijde :) ) a na dynamické pole objektů jsem si vypůjčil vector z knihovny STL. Ale nemějte strach, nepoužívám v programu žádné "vychytávky" a snažil jsem se ho napsat co možná nejjednodušeji. Nemusíte mít strach vše je vysvětleno.

V programu používám dva časovače popsané v minulém díle. První na výpočet FPS a druhý na ty vektory.

Vektory (pouze 2D)

Abych nemluvil do větru tak nejdřív si povíme něco málo o našem objektu, který jsem nazval CObject.

//definujeme si objekt jako tridu
class CObject {
private:<

public:
	//zduvodu pristupu a ne pres clenskou fci jako GetX(); 
	//proto jsou verejne
	float x,y;//pozice objektu
	float cx,cy;//pro vektor ve 2D

		CObject() {};//kostruktor

	~CObject() {};//destruktor

	void Init()
	{
		x=rand()%640;
		y=rand()%480;
		cx=rand()%200+1;
		cy=rand()%200+1;
	}

	//posouva prvky a zalezi kolik ubehlo casu 
	void Move(float cas)
	{
		//vypocita o jak velky kus posunout souradnice
		//je to pres vektory cx a cy = posunuti za 1s
		x+=cx*(cas/1000);
		y+=cy*(cas/1000);

		//kotroluje x-ovou
		if (x<0)
		{
			x=0;
			cx=-cx;
		}
		if (x>620)
		{
			x=620;
			cx=-cx;
		}
		//kontroluje y-ovou
		if (y<0)
		{
			y=0;
			cy=-cy;
		}
		if (y>460)
		{
			y=460;
			cy=-cy;
		}
	}
};

Private proměnné nejsou z důvodu, abych nemusel psát funkce na vrácení x, y, cx a cy a proto je vše public. Proměnné x, y jsou pozice obrázku nebo čtverečku na obrazovce. cx a cy je vektor. Např. cx=cy=50 to znamená, že se obrázek za jednu vteřinu posune o 50 pixelů doleva a dolů

Konstruktor a destruktor jsou bez funkce. Funkce (nebo lépe metoda) Init se nám postará o inicializaci náhodných souřadnic a náhodného směru a rychlosti objektu. Je volána vždy při vytvoření nového objektu.

Než si popíšeme metodu Move tak nejdřív vysvětlím jaká je finta v programu a proč tomu říkám vektory (teď nemyslím vector z STL). Jak jsem před chvílí zmínil tak např. cx znamená velikost posunutí za 1s. Nyní, ale co když demo běží jednou 50 FPS a podruhé 150 FPS co pak?
Jako první si zjistíme kolik uběhlo času od posledního vykreslení scény na obrazovku. K tomu nám slouží druhý časovač.

Rovnice na vypočítání posunuti.

cx=50 a představme si, že hra běží pouze 2 FPS. Tedy uběhlo od posledního vykreslení 500 ms = 1/2 s. V proměnné cas tedy máme hodnotu 500, protože jsme časovač pustili na volání po každé milisekundě. Podělíme tedy cas/1000 a dostaneme jakoby část vektoru, o který se máme posunout. Tedy o 0,5cx. A když hra beží 2 FPS tak nám vyjde, že cx=2*(0,5cx) a to je pravda. 

Pro obě souřadnice vypadá výpočet takto:

posunutí x =cx*(cas/1000);
posunutí y =cy*(cas/1000);

To pak přičteme k x a k y. S tím, že cx nebo cy může být i záporné číslo. A pomocí tohoto přepočtu nám nezáleží na velikosti FPS v programu a čtverečky se budou posouvat o stejný kus.

Metoda Move dostane jako parametr čas, který uběhl od posledního volání této funkce a vypočítá posunutí. Pak zkontroluje jestli čtvereček není mimo scénu a pokud je obrátí vektor. Např. x vyjde -5 tak nastavíme x na 0 a vektor, který mohl být cx=-35 převrátíme na cx=-cx tedy cx=35 a tím dosáhneme efektu odražení.

To by bylo k vektorům nebo posunům nebo synchronizaci scény vše. Toto se používá už dlouho a samozřejmě dodnes ve hrách jenom je to o trochu složitější a to ještě díky tomu, že se objevujeme ve 3D prostoru. Malinko si popíšeme samotný program.

Jako dynamické pole používám šablonu vector z STL ( moje oblíbená :-D )

vector<CObject> v;//nas vektor pro dynamicky seznam
CObject objPrac;//pracovni objekt pro pridavani do vektoru

v je tedy naše proměnná pro práci s polem a objPrac je pomocná proměnná, kterou přidáváme další objekty do dynamického pole.

Před samotnou smyčkou programu si vytvoříme alespoň 10 čteverčů, aby obrazovka nebyla po spuštění prázdná.

	//dame jeden objekt na scenu alespon
	for(i=0; i<POCET_NA_ZACATKU;i++)
	{
		objPrac.Init();
		v.push_back(objPrac);
	}

Tedy v našem pracovním objektu objPrac zavoláme metodu Init, která vytvoří náhodný objekt (souřadnice a vektor) a pak už jenom pomocí metody vectoru push_back přidáme nový prvek na konec pole.

V samotné smyčce programu vykreslujeme v cyklu všechny prvky pomocí této smyčky.

	for (i=0;i<v.size();i++)
		{
			v[i].Move((float)cas);
			blit(obr,bb,0,0,(int)v[i].x,(int)v[i].y,20,20);
		}

Nejdříve tedy prvek posuneme a pak vykreslíme.

Pak program umožňuje několik málo funkcí s objekty jako je přidání a odnětí.

		//pridej
		if (key[KEY_SPACE])
		{
			objPrac.Init();
			v.push_back(objPrac);
			rest(20);
		}
		//uber
		if (key[KEY_ENTER])
		{
			v.pop_back();
			rest(20);
		}
		//uber vsechny
		if (key[KEY_DEL])
		{
			v.erase(v.begin(),v.end());
		}
		//pridej 100
		if (key[KEY_INSERT])
		{
			for(i=0;i<100;i++)
			{
				objPrac.Init();
				v.push_back(objPrac);
			}
			rest(50);
		}

Tělo podmínky pro SPACE je stejné jako u inicializace. 
ENTER místo push_back volá pop_back což je odstranění prvku ze seznamu, který je na konci. 
DELETE vymaže celý seznam metodou erase. Ta má dvě varianty buď maže nejáký prvek nebo nekolik prvků za sebou. My volíme variantu číslo dvě a tedy chceme vymazat celý seznam a proto voláme begin() což je adresa začátku a end() což je konec seznamu ve vectoru. Metody begin a end vracejí interator. Ale myslím, že pokud se o toto téma zajímáte tam na tomto serveru vychází ještě seriál o C++ včetně STL (Standard Template Library), ale na druhou stranu jsem nepoužil nic co by se nedalo z příkladu pochopit. Tím myslím, že pro dnešní příklad jste ani vectory nemuseli znát a vše vám bylo jasné, protože nepoužívám žádné programátorské bombónky a navíc dynamické pole se dájí obejít přes lineární seznam, kde v objektu je pointer na další objekt stejného typu, což se používalo převážně v C, ale někdy i v C++. Ale vectory jsou lepší a ušetří Vám  práci a hlavně nervy
A jako poslední je INSERT, který přídá 100 prvků do seznamu za sebou v cyklu.

Nakonec v programu následuje odinicializace a vrácení běhu operačnímu systému.

Vím, že program o 200 řádcích se dá jen těžce vysvětlit, ale snažil jsem se dávat všude komentáře a tak doufám, že jste všechno pochopili.

PS: Doufám, že jsem Vás vectorem z STL a třídou class moc nepřekvapil ;-).

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

Tématické zařazení:

 » Rubriky  » C/C++  

 

 

 

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

 

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

Uživatelské jméno:

Heslo: