Určite ste niekedy hrali niektorú z klasických 2D počítačových hier ako napríklad Super Mario alebo Prince of Persia. Skrátka hry, v ktorých pobehujú nejaké postavy, povaľujú sa nejaké predmety a hlavného hrdinu treba niekam bez ujmy na zdraví dopraviť. Tiež si možno pamätáte na korytnačky z Comenius Loga, ktoré boli miestami priesvitné a ktoré mohli pobehovať po pracovnej ploche bez toho, aby ju poškodili. V oboch týchto prípadoch (a v mnohých ďaľších) prichádzajú ku slovu _sprity_ (čítaj sprajty). Sprite (po anglicky škriatok alebo strašidlo) je obrázok, ktorý nemusí mať nutne obdĺžnikový tvar a ktorý môžete vykresliť kdesi na obrazovku. Sprity fungujú tak, že sa určí nejaká farba, napríklad fialová ([255,0,255]), a tá sa bude pokladať za priesvitnú. Miesta, kde je v obrázku fialová, sa pokladajú za diery a pri použití blitu sa neskopírujú. (Táto špeciálna farba sa volá _color key_.) V SDL môžete používať color key, aký chcete, dokonca môže byť v každom obrázku iný (to sa neodporúča, lebo v tom nastane úplný chaos). Dôsledok je, že ho v každom obrázku treba nastaviť zvlášť. Preto urobím novú verziu funkcie loadImage: def loadImage(filename, isASprite = false) surface = Surface.load_new(filename) surface.convert! surface.set_colorkey([255,0,255], RLEACCEL) if isASprite surface end Na pripomenutie, isASprite = false znamená, že isASprite je nepovinný a keď ho pri volaní loadImage nezadáte, predpokladá sa false. p(advanced). Ten druhý parameter pre set_colorkey sú nastavenia. Funguje iba jediné nastavenie, RLEACCEL, ktoré z obrázku urobí tzv. _RLE sprite_. RLE sprity sa ukladajú v pamäti tak, že miesto toho, aby za sebou bolo veľa fialových bodov, dá sa tam značka "tu je toľko a toľko priesvitných bodov". To znamená, že jednak bude ten obrázok zaberať v pamäti trochu menej miesta, hlavne sa ale bude rýchlejšie kresliť. (Na druhú stranu, RLE obrázky sa kvôli tomu dosť zle upravujú, a keď obrázok chcete neskôr upravovať, použitie RLE nemusí byť ten najlepší nápad.) Budeme ešte potrebovať ďalšiu novú funkciu keyIsPressed. Bude podobná funkcií keypress, ale zatiaľčo tá čaká na stlačenie klávesu a nič nerobí, táto bude zisťovať, či je niečo stlačené. def keyIsPressed event = EventQueue.poll while event != nil exit if event.is_a? QuitEvent return true if event.is_a? KeyDownEvent event = EventQueue.poll end return false end Na nasledujúcom príklade si ukážeme, ako sa dá s pomocou spritov spraviť jednoduchá animácia. Okrem programu budete potrebovať päť obrázkov sln1.tga, sln2.tga, sln3.tga, sln4.tga a sln5.tga (všetky majú rozmer 100x100 bodov). Prečítajte si pozorne program a najmä komentár, ktorý po ňom nasleduje. pozadie = Surface.new([640, 480]) 0.upto(320) { |i| pozadie.line [0, i], [640, i], [128 + i/5, 128 + i/5, 255] } 320.upto(640) { |i| pozadie.line [0, i], [640, i], [0, 255 + 320-i, 0] } slnko = [] (1..5).each { |i| slnko << loadImage("sln#{i}.tga", true) } i = 0 while !keyIsPressed $screen.blit(pozadie, [0, 0]) $screen.blit(slnko[i], [400, 100]) $screen.flip i = (i + 1) % 5 Timer.delay(100) end Takže teraz postupne po častiach: pozadie = Surface.new([640, 480]) 0.upto(320) { |i| pozadie.line [0, i], [640, i], [128 + i/5, 128 + i/5, 255] } 320.upto(640) { |i| pozadie.line [0, i], [640, i], [0, 255 + 320-i, 0] } Najprv príkazom Surface.new v pamäti vytvorím novú plochu veľkú 640x480 bodov. Potom do nej nakreslím oblohu (vodorovné čiary, prechod z bledomodrej do ešte viac bledomodrej) a trávnik (prechod od zelenej do tmavozelenej). slnko = [] (1..5).each { |i| slnko << loadImage("sln#{i}.tga", true) } Potom si urobím pole slnko a dám do neho sprity zo súborov sln1-5.tga. (pole << vec tú vec prihodí na koniec poľa.) Keď sme si takto vytvorili pozadie, môžeme sa venovať spritom. Päť spritov, ktoré máte k dispozícii vytvára animáciu slniečka. Keby sme ich však na obrazovku plácali jeden cez druhý, tie staré by nezmizli a bol by z toho chaos. Preto sa animácie robia väčšinou tak, že sa premaže stará situácia (buď to celé preplácnem pozadím, alebo len tú časť, kde sú predmety) a až potom sa tam nakreslia jednotlivé pohyblivé predmety (v tomto prípade iba slniečko). while !keyIsPressed $screen.blit(pozadie, [0, 0]) $screen.blit(slnko[i], [400, 100]) $screen.flip i = (i + 1) % 5 Timer.delay(100) end Hlavný cyklus beží, kým niekto nestlačí nejaký kláves. Najprv na obrazovku dám pozadie a potom príslušné slniečko. Premennú i, kde si pamätám, ktoré slniečko sa má zobraziť, zvýšim tak, aby postupne mala hodnoty medzi 0 a 4. Nakoniec počkám 100 milisekúnd, aby to nebežalo príliš rýchlo. *Úloha č. 1:* Napíšte, skompilujte, pochopte. *Úloha č. 2:* Ako to pobeží, ak vynecháte príkaz Timer.delay? Čo to spraví, ak budete slniečko kopírovať na pozíciu [400,350] miesto na [400,100]? *Úloha č. 3:* Pridajte vedľa slniečka ešte aj druhé, ktoré sa bude tiež otáčať. *Úloha č. 4:* Upravte úlohu č. 3 tak, nech sa každé otáča iným smerom. *Úloha č. 5:* Vykreslite na obrazovku 40 náhodne umiestnených slniečok zo súboru sln1.tga. (Nebudú sa otáčať.) Dávajte si pozor, aby ste ich neumiestnili na trávnik. *Špeciálna úloha:* Urobte to tak, nech sa to slniečko presúva po kruhovej dráhe. h3. Dodatky h4. Používanie Timer.delay V nepredvídateľne dlhých cykloch (t.j. tých, ktoré môže skončiť len užívateľ), je veľmi dôležité dať tam nejaký Timer.delay, a nie len preto, aby to nešlo príliš rýchlo. Inak totiž programy strašne vyťažujú procesor. Timer.delay síce podľa svojho popisu má program trochu spomaliť, ale na druhú stranu sa počas neho môže procesor venovať iným veciam. Napríklad mimo iného sledovať, či užívateľ niečo robí. Ak tam Timer.delay nedáte, tak to sledovať nestíha, takže program nereaguje, a užívateľovi pripadá, že je pomalý. V skratke: Timer.delay síce program spomalí, ale užívateľovi sa môže zdať, že sa zrýchli.