int21h

Multimédia v Pascalu pro pokročilé

Protože jsem kdysi probíral seriál o grafice ve Free Pascalu a zmínil tam okrajově jisté jednotky, a protože zvuk jen čistě pod MS-DOSem či Windows 98 v Turbo Pascalu 7 asi nikoho neohromí, rozhodl jsem se (abych také nevypadal tak blbě, že nic nepíši), udělal si trochu času, a začal psát seriál o využití externích jednotek Free Pascalu. Sice už jedna z nich byla nakousnutá, ale popis byl v C a já bych se rád věnoval Pascalu.

Rozhodně se nebudu věnovat Direct X (a v něm obsažených Direct Draw či Direct Sound), které si můžete stáhnout z adresy (asi už to tam bude někde hodně zastrčené):

http://www.delphi-jedi.org

protože je to dost složité a i když to dokáže ovládat všechno, co ke hrám potřebujete (grafiku, zvuky, myš, joystick, síť) a existuje k tomu alespoň maličké rozhraní FPCX (ke stáhnutí na adrese http://ainc.de), jsou mnohem snažší způsoby, jak programovat kompletní hry. Jednotky, kterým se budeme věnovat, totiž umí nejen grafiku a zvuky, ale také spoustu dalších věcí, které ke hrám jistě použijete. Dvě jednotky, které si probereme, jsou určeny jen pro Windows (a jedna z nich dokonce výše zmíněné DirectX využívá), další je multiplatformní (SDL) a funguje současně (po dvojím překladu) jak pod Windows, tak i pod Linuxem, a využívá (pravděpodobně) ke zvukům pod Windows následující jednotku FMOD:

Té jsem se chtěl věnovat jako první, než jsem zjistil, že má rozhraní psané pouze v C, takže bych toho z ní tolik nevyčetl. Bohužel ale umí jen zvuky, takže pro hru k ní potřebujete ještě doplnění další jednotkou, která umí grafiku (jako např. tu, kterou si probereme v tomto díle seriálu - ještě budou dva), a kromě ní samozřejě také čtení I/O zařízení (myš, klávesnice, joystick a síť). Tato zvuková jednotka se dá stáhnout z adresy http://www.fmod.org. K využívání jejich funkcí musíte mít u svého programu přiloženu knihovnu FMOD.DLL (která je součástí balíku), nebo ji nahrát do adresáře WINDOWS. Pokud se jí chcete zabývat (je opravdu dobrá), obsahuje nejen suprově napsanou nápovědu ve stylu Free Pascalu, kde je nejen rozebrán každý příkaz, ale také tutorial, který Vás provede krok za krokem všemi funkcemi (pokud umíte anglicky). Jak napsat program pochopíte za 30 vteřin (možná by o tom mohl někdo napsat návod do INT21h). Kromě výše uvedených věcí se s ní dodává velká spousta příkladů ve zdrojových kódech, které jsou také už přeloženy do EXE a rozhodně fungují! Je to prostě super jednotka pro Windows, která zvládá streamy z disku i většinu rozšířených formátů (MP3, OGG, MOD, WAV, S3M, IT, XM, MID, MP2, atd.) v závislosti na tom, co umí Windows. A jak jsem se právě dozvěděl, nová verze 4 už podporuje 13 různých platforem včetně Linuxu, Playstation, XBoxu, Macintoshe, atd.


Nás v dnešním díle čeká velmi jednoduchá grafická jednotka VGFX-vesa3, která se dá stáhnout na adrese http://www.venomsoftware.de/various.htm (na těchto stránkách je ke stažení ovšem halda mnohem efektních programů a dem, např. VGFX_OpenGL). K její činnosti budete potřebovat knihovnu FPCX.DLL (je přiložena). Jednotka je psaná pro staré verze FPC, takže v nových 2.X verzích musíte použít přepínač {$callingoldfpccall}. U jednotky verze 2002/08/01 je také chyba v jednotce načítající PCX (nedokáže si poradit s nejnovějšími PCX), ale je tu uvedena i oprava (bohužel v C). Jak je tomu v nové verzi nevím, protože jsem líný to zjišťovat :-). Je možné, že už je obojí opraveno (na stránkách už myslím je i verze pro 2.X). K jednotce se dodává jeden font (FNT), dokumentace ve formátu DOC (která Vám ukáže, jak napsat svůj první program), základní rozhraní a tyto hlavní jednotky (pro ty, kdo neumí anglicky):


	VGFX		-	hlavní jednotka
	VGFX_2D		-	2D věci (pixely, čáry, atd.)
	VGFX_Sprites	-	obrázky (rotace, alfa kanál, rotace, velikost)
	VGFX_Text	-	psaní textů (fonty FNT nebo sprity)
	VGFX_Files	-	I/O operace se soubory
	VGFX_Win32	-	komunikace s Windows  

Pokud začnete s obhlídkou těchto jednotek, asi si moc nepomůžete, neboť většina funkcí postrádá popisky (pravda, u některých jsou). Navíc je autor Němec, takže 50% těch, které tam jsou, nám jsou k ničemu. Můj návod se však může hodit těm, kteří neumí ani anglicky, nebo nepochopili přiložený tutorial (což je velmi nepravděpodobné: když jsem ho pochopil já, tak snad asi každý). Průměrně zdatné oko ale už nahlédnutím pochopí, k čemu přibližně slouží některé funkce (bráno odshora přes jednotlivé jednotky):


	GetPixel, PutPixel	-	pracují s RGB složkami typu BYTE
	PutPackedPixel		-	pracuje s WORDem
	Clipped/Packed		-	zatím nemám tušení
	Line			-	vykreslí čáru
	LineSmoother		-	zřejmě vyhlazená čára
	Horz/Vert		-	rychlejší kreslení čáry (?)
	Rectangle		-	kreslí obdelník
	Circle			-	kreslí kružnici
	Full/Fine		-	kruh a jemné vyhlazení?
	Alpha			-	práce s průhledností (?)


	File2Mem		-	nahraje obrázek do paměti
	BigFile2Mem		-	práce s větším obrázkem
	Spr			-	sprity (?)


	Load_xx			-	nahraje obrázek z disku
	Save_Sprite		-	uložení obrázku (?)
	Put/GetSprite		-	kopírování mezi RAM a obrazovkou
	Clipped			-	ořezávání (?)
	HC			-	průhledné obrázky (?)
	PutBrightSprite		-	změní jas obrázku při zobrazování
	PutMixedSprite		-	zkombinuje obrázek+pozadí a pak DIV 2
	PutTransSprite		-	změní jas pozadí a pak zkombinuje s obrázkem
	PutAlphaSprite		-	zobrazí průhledný obrázek
	FadeSprite		-	zkombinuje dva obrázky (?)
	ScaleSprite		-	změní velikost obrázku
	AdjustSpriteBrightness	-	změní jas obrázku
	Decrase/Incrase		-	změní jas jedním či druhým směrem
	CopySprite		-	zkopíruje obrázek
	Rotate_Scale_Sprite	-	otočí obrázek a změní velikost
	Rotate_Sprite180	-	zrcadlí obrázek


	Load_Fonts		-	nahraje fonty
	Big			-	zřejmě práce s většími daty
	Set/GetTextSize		-	velikost textu
	MoveTo			-	nastavení souřadnic
	GetX/Y			-	zjištění souřadnic
	OutText/XY		-	výpis textu (jako u GRAPH)
	1/2			-	dva různé styly (?)
	TextATxx		-	hladší vykreslování (?)


	Terminate		-	ukončení programu
	Keypressed		-	je stisknuta klávesa?  

Netvrdím ale, že jsem pochopil správně, co má daná funkce dělat. V dokumentaci se dočtete, co znamenají jednotlivé přívlasky:


	HC	-	tzv. Hide Color, nebo-li vynechávací průhlednost
	Clipped	-	ořízne obrázek, pokud se nevejde na obrazovku
	Bright	-	mění jas obrázku
	Mixed	-	mixuje obrázky
	Trans	-	průhlednost
	Alpha	-	práce s alfa kanálem  

Dobře, tak se pustíme do našeho prvního programu. Autor zde přikládá ukázkový soubor, takže se ho budeme držet. Nejprve musíme nastavit nějaké direktivy:


{$ASMMODE INTEL}
{$APPTYPE GUI}
{$MODE FPC}
{$RANGECHECKS OFF}
{$Q-}
{$R-}
{$S-}
{$D-}  

Tady bych asi použil nějaké svoje. Pak následuje typické "PROGRAM" a pak si nadefinujeme vlastnosti našeho okna (i pokud poběžíme ve Full Screen), včetně názvu titulku okna, který se zobrazí buď nahoře, nebo dole na liště při minimalizaci:


const	ApplicationID : PChar = 'Grafika';
	VelikostX = 800;
	VelikostY = 600;  

Dále budeme muset použít jednotky:


uses	Windows,VGFX_2D,VGFX_Sprites,VGFX_Text,
	VGFX_win32,VGFX_Files,VGFX_Errors,BigFile2,SysUtils;  

Až doposud asi všechno jasné. Teď si budeme muset nadefinovat tzv. virtuální okna. Každé pozadí, obrázek, sprite nebo obrazovka je okno. Pokud budeme chtít v našem programu mít dva obrázky, musíme nadefinovat toto:


var	Sprite1,Sprite2,Pozadi,Result : VirtualWindow;  

POZADI je pracovní plocha, kdežto RESULT je samotná obrazovka. Protože jsme ve Windows (existuje ale i verze pro OS/2), budeme si nejprve muset vytvořit nějaké své grafické okno. Zde si ho jen zaregistrujeme a poté inicializujeme grafiku v nějakém rozlišení (vždy to bude 16 bitový režim):


Window_RegisterClass(WIN_Normal);	{prý jsou tu třídy i pro spořiče}
Window_Main_Handle:= Window_CreateClass(ApplicationID,WIN_Normal);
CheckWMCreate;
Init_Graph(VelikostX,VelikostY);  

Trochu (dost) překáží, že pokud máte na ploše 1024x768 a zde nastavíte 800x600, při ALT+TAB se Vám neobnoví 1024x768 (navíc po skončení programu jsou posunuta okna, která nebyla maximalizována). Každé virtuální okno musí být inicializováno. To se provede buď tak, že do něj nahrajete obrázek (ovšem nesmí být před tím inicializováno; druhý parametr je '' protože nenahráváme z velkého souboru - co to znamená zatím nevím), nebo mu nastavíte rozměry. Rovnou si také nahrajeme fonty:


Load_Pcx('Image.pcx','',Sprite01);
Load_Pcx('firstsprite.pcx','',Sprite02);
Init_VW(Result,VelikostX,VelikostY,True);
Init_VW(Pozadi,VelikostX,VelikostY,True);
Load_Fonts('c:programFonts.fnt');  

Nyní máme v paměti dva obrázky a dvě prázdná okna. Program nám poběží ve smyčce, ve které budeme vykreslovat. Smyčka se ukončí tehdy, pokud naše okno někdo zavře, nebo se klikne levým tlačítkem myši (m_r by byl test na pravé; m_x a m_y vrací souřadnice). KTASTE vrací True, pokud je stisknuta daná klávesa (13 je Enter, 27 je ESC, X je X, atd.).


repeat
 {zde budeme něco dělat}
until ApplicationRunning or KTaste[13] or m_l;  

Dobře, ale jak kreslit. V příkladu autora je použita ještě proměnná "i", takže si ji nadefinujeme jako INTEGER (proč ne LONGINT?) a před cyklem REPEAT ji nastavíme třeba na 100. V cyklu pak použijeme následující funkce:


Flip_VW(Pozadi.VWOffset,Result.VWOffset);
Inc(i);
if (i > 600) then i := 100;
PutSprite(Result,Sprite02,i,220);
Flip_DD(Result.VWOffset);
GETALLMESSAGES;
HANDLEMINIMIZED;  

První řádek umístí pozadí (přepíše okno, zde prakticky vymaže) do okna RESULT (to se samozřejmě může jmenovat i jinak). Další dva řádky mění X souřadnici na obrazovce, protože budeme náš obrázek posouvat. Dále je zde funkce, která umístí SPRITE2 na pozici [i,220] do okna RESULT. Funkce FLIP_DD zobrazí okno RESULT na monitor. Poslední dvě funkce jsou tu proto, abychom spolupracovali s Windows (čteme jeho zprávy (události myši, klávesnice, okna) a reagujeme na minimalizaci). Toto celé se opakuje, tj. nejprve se smaže pozadí, vykreslí se do něj obrázek a pak se to celé vykreslí na obrazovku. Až budeme chtít program ukončit, jednoduše zrušíme veškerá okna, pak grafiku, poté podle rady autora "zkontrolujeme paměť" a ukončíme se:


Kill_VW(Pozadi);
Kill_VW(Sprite01);
Kill_VW(Sprite02);
Kill_VW(Result);
Kill_Graph;
CheckMemory;
TERMINATE;  

To nebylo složité, že? Dobře, ale autor nám tu zanechal ještě demo. Toto demo zobrazí obrázek na pozadí, rotuje a zmenšuje/zvětšuje sprite a okolo toho krouží ještě průhledný další sprite. Protože autor umístil 1 obrázek na jisto tak, aby "nevyčuhoval" z obrazovky, nepoužívá u něj Clipped funkce (je také ale nutné dodržet dané rozlišení). Tento program se velmi podobá příkladu. Jen je zde navíc jednotka KEYBOARD a dvě další proměnné sloužící k rotaci a změně velikosti, a proměnná pro jednotku ovládající klávesnici z FP:


var	Rotation : longint;
	Scaling : longint;
	key : TEvent;  

Základ programu vypadá více méně stejně:


begin
 Window_RegisterClass(WIN_Normal);
 Window_Main_Handle:= Window_CreateClass(ApplicationID,WIN_Normal);
 CheckWMCreate;
 Init_Graph(VelikostX,VelikostY);
 Load_Fonts('Fonts.fnt');
 Load_Pcx('Image.pcx','',Sprite01);
 Init_VW(Result,VelikostX,VelikostY,True);
 Init_VW(Pozadi,VelikostX,VelikostY,True);	{True = vyplnit na černou barvu}


  {... nová část 1 ...}


 repeat
   Flip_VW(Pozadi.VWOffset,result.VWOffset);


   {... nová část 2 ...}	


  Flip_DD(Result.VWOffset);
  GETALLMESSAGES;
  HANDLEMINIMIZED;
 until not ApplicationRunning or KTaste[27] or m_l;
 Kill_VW(Pozadi);
 Kill_VW(Sprite01);
 Kill_VW(result);
 Kill_Graph;
 CheckMemory;
 TERMINATE;
end.  

Tady je myslím asi všechno jasné. A teď ty nové věci. Bude tu pár nových příkazů. Nejprve první část:


 ScaleSprite(Sprite01,Pozadi);
 Kill_VW(Sprite01);
 Load_Pcx('Image2.pcx','',Sprite01);
 SetTextSize(1);
 OutTextXY(BackGround,10,10,31,63,31,'Pokusny graficky program');
 PutPixel(Background,VelikostX-50,20,65520,65520,65520);
 PutPackedPixel(Background,VelikostX-45,20,65520);
 PutClippedPixel(Background,VelikostX-40,20,65520,65520,65520);
 CursorVisible(True);  

První příkaz zvětší obrázek ze SPRITE1 a uloží ho do POZADI. Tímto obrázkem se tedy bude každý snímek znovu vyplňovat obrazovka. Další příkaz tento obrázek smaže, neboť již nebude potřeba. V zápětí do něj nahrajeme nový obrázek (sprite). Pak nastavíme velikost textu a vypíšeme nějaký text na souřadnice [10,10] barvou 31,63,31 (RGB). Následují tři různé metody umístění pixelu. Já jsem v tom osobně nepoznal rozdíl (ani ty pixely nevidím). Poslední příkaz nařizuje, aby byl kurzor myši stále viditelný. To byla první část. Stále ještě srozumitelná (více méně). Jdeme na druhou novou část:


   Rotation := GetTickCount div 10;
   Scaling := 10+Abs(Sin((GetTickCount/300000)*360)*400);
   Rotate_Scale_SpriteHC(Sprite01,result,result.BreiteDiv2,
                         result.HoeheDiv2,Round(Scaling),
                         Round(Scaling),Rotation,0);
   PutAlphaClippedHCSprite(result,Sprite01,
                           result.BreiteDiv2-Sprite01.BreiteDiv2+
                           Round(Sin((GetTickCount/200000)*360)*200),
                           result.HoeheDiv2-Sprite01.HoeheDiv2+
                           Round(Cos((GetTickCount/200000)*360)*200),0);
   OutTextXY(result,550,VelikostY-30,255,255,255,'Pozice X mysi : '+LongintStr(m_x));
   OutTextXY(result,550,VelikostY-10,255,255,255,'Pozice Y mysi : '+LongintStr(m_y));  

Na prvním řádku získáme počet milisekund, po které naše aplikace běží. Vydělením 10 získáme nějaký úhel. Na dalším řádku vypočteme velikost obrázku. Ovšem toto je pouhá matematika, která nemá s grafikou nic společného, takže tyto údaje můžete získat i vlastní cestou (např. z tabulky). První nová funkce obrázek natáčí a zároveň i mění jeho rozměry. Obrázek se rovnou ukládá do obrazovky, takže původní data zůstávají nedotčena (BreiteDiv2 je zřejmě velikost X dělena dvěma). Můžete měnit nezávisle velikost X a Y, ale to se zde neděje. Druhá funkce kreslí ten samý obrázek, ale průhledně a přitom ho nechává obíhat po kružnici (Clipped je použito, protože obrázek občas přečuhuje přes okraj; a díky HC není vůbec vykreslena černá barva, čili 0). Opět matematika, která není potřebná ke zobrazování (když tam dosadíte nějaké statické X a Y, bude to fungovat také). Poslední dva řádky jen vypisují informace o aktuální pozici myši. Funkci LONGINTSTR snad není potřeba vysvětlovat (viz. procedura z FP jménem STR). Trochu mi není jasné, na co autor potřeboval jednotku KEYBOARD. Tak, to by bylo všechno. Kdyby se našel někdo, kdo by chtěl popsat tuto jednotku podrobněji (nebo její novější verzi), budu jen rád.


Příště se podíváme na LX5SUITE a pak JediSDL (u něj si ale musíte všechny příklady sami přeložit, zatímco LX5SUITE je má již přiloženy a navíc se zdá být i jednodušší, ovšem zase není pro Linux). Takže to zatím vstřebejte. Problém v těchto jednotkách je jediný: všechny jsou super, ale vybrat si musíte jen jednu z nich (max. 2, pokud kombinujete grafiku a zvuky). A to je velká škoda. Rozhodně ale přijdou k duhu, jelikož např. RAIN funguje pouze s Turbo Pascalem 7 (ne, že by samotný RAIN nefungoval s něčím jiným, ale zřejmě jeho jednotka RAIN.PAS, která má zajišťovat komunikaci v Borland Pascalu 7 nebo Free Pascalu obsahuje nějakou chybu (např. už chybějící čárka znemožňující kompilaci nevěští nic dobrého), kvůli které to prostě nehraje). Příště (kdy to bude, to je ale už ve hvězdách... čas došel) už to nebude tak jednoduché :-)

2006-12-06 | Martin Lux
Reklamy:
„Mít rád lidi a milovat lidi to je celé tajemství a snad jediný recept na štěstí.“ Jan Werich