Lua + LÖVE: vytvořte si vlastní hru

26. 5. 2009
Doba čtení: 12 minut

Sdílet

Ve dvanácté části seriálu o programovacím jazyku Lua se seznámíme se souborem modulů (knihoven) LÖVE, pomocí nichž je možné vytvářet různé multiplatformní interaktivní aplikace, především hry. Velkou předností LÖVE je snadná použitelnost, takže se do vytvoření jednodušší hry mohou pustit i začátečníci.

Obsah

1. Stručný popis systému LÖVE
2. Moduly obsažené v systému LÖVE
3. Nastavení grafického režimu, základy práce s 2D grafikou
4. Podpora přehrávání zvuků a hudby
5. Použití časovače – základ interaktivních her a animací
6. Čtení stavu klávesnice
7. Práce s myší
8. Práce s joysticky
9. Odkazy na Internetu

1. Stručný popis systému LÖVE

Systém LÖVE je určen pro jednoduchou a především rychlou tvorbu her s 2D grafikou, hudbou a zvuky, které jsou naskriptované v programovacím jazyce Lua, popř. se v nich využívá pomocných funkcí naprogramovaných v céčku či C++. Velkou předností systému LÖVE je jeho snadná použitelnost, spočívající zejména v přehledném aplikačním programovém rozhraní (API) jednotlivých knihoven přítomných v tomto systému i jednoduché syntaxi jazyka Lua a jeho dynamickém typovém systému. Samotný systém LÖVE interně využívá několika céčkových a C++ knihoven, především boost (obecné algoritmy v C++), SDL (nastavení grafických režimů), SDL_mixer (podpora hudby a zvuků), OpenGL (2D grafika, double buffering), DevIL (práce s rastrovými obrázky) a FreeType 2 (vykreslování písma), avšak vývojář, který pomocí LÖVE vytváří hry či jiné interaktivní aplikace s 2D grafikou, hudbou a zvuky, je od těchto knihoven zcela odstíněn aplikačním programovým rozhraním systému LÖVE.

V současnosti je systém LÖVE podporován jak na Linuxu, tak i na operačních systémech Microsoft Windows XP, Vista (lze jej však používat i na Microsoft Windows 98 SE, ovšem jen při překladu ze zdrojových kódů) i Mac OS X. Pro GNU/Linux, přesněji pro distribuce založené na Debianu, tj. i pro Ubuntu, je k dispozici instalační balíček. Instalační program existuje i pro operační systémy Microsoft Windows, což znamená, že pro vytváření her v tomto systému ani není nutné, aby byl v systému přítomný překladač C či C++, což použití LÖVE na těchto operačních systémech zjednodušuje (samotný LÖVE již v tomto případě obsahuje interpretr jazyka Lua i všechny požadované knihovny). Pro Mac OS X se instalační program teprve připravuje, před jeho dokončením je však možné provést instalaci překladem zdrojových textů – při překladu se však předpokládá, že jsou v systému již přítomny všechny potřebné knihovny (viz on-line dokumentace na domovské stránce systému LÖVE, odkaz na ni je uveden v deváté kapitole).

2. Moduly obsažené v systému LÖVE

Celý systém LÖVE je rozdělen do několika modulů (knihoven), jejichž jména vždy začínají prefixem love., například love.joystick či love.system (nejedná se samozřejmě o nic jiného, než o jmenný prostor implementovaný tak, že jsou všechny knihovny uloženy v globálním asociativním poli pojmenovaném love). Tyto moduly je možné do značné míry používat nezávisle na sobě. Například pro aplikace, které pouze vyžadují základní práci s grafikou, tj. načítání a úpravu rastrových obrázků, vykreslování základních rovinných útvarů, vytvoření okna či přepnutí grafického režimu a další podobné operace, postačuje použít jen knihovnu love.graphics; pro programy s animacemi se navíc využívá knihovna love.timer a při požadavcích na interaktivitu (tj. reakcí aplikace na operace prováděné uživatelem pomocí klávesnice, myši, touchpadu, joysticku či jiného vstupního zařízení) má programátor na výběr funkce umístěné v knihovnách love.mouse, love.keyboard a love.joystick atd. V následující tabulce jsou vypsány všechny moduly, jenž jsou v současné verzi systému LÖVE programátorům nabízeny:

Název modulu Nabízené funkce
love.graphics práce s okny, nastavení celoobrazovkového režimu, povolení či zákaz antialiasingu, vykreslování rastrových obrázků i základních geometrických tvarů, využití částicových systémů
love.audio základní práce s hudbou a zvuky, spuštění, pozastavení a zastavení přehrávání, nastavení počtu zvukových kanálů, načítání hudby ze souborů typu WAV, AIFF, MP3, OGG, MIDI, MOD, XM i dalších formátů používaných hudebními trackery (viz paralelně běžící seriál o architekturách počítačů)
love.physics řešení kolizí a popř. i vzájemných odrazů konvexních rovinných útvarů, seskupování těchto útvarů do těles (anglicky body)
love.mouse práce s myší – nastavení viditelnosti a pozice kurzoru, programový posun kurzoru na zadané souřadnice, zjištění aktuální pozice kurzoru myši
love.keyboard práce s klávesnicí – zjištění, zda je nějaká klávesa stlačena či nikoli (tato „knihovna“ ve skutečnosti obsahuje pouze jednu funkci, která například rozlišuje mezi pravým a levým Shiftem, numerický blok klávesnice atd.)
love.joystick podpora pro různé typy joysticků či podobných vstupních zařízeních (více na sobě nezávislých os, tlačítek a dalších ovládacích prvků)
love.filesystem načítání a ukládání souborů uložených v adresáři (adresářích) hry; přístup k těmto souborům je tedy jednodušší, než při použití standardního modulu (knihovny) io
love.timer na operačním systému nezávislý časovač s rozlišením jedné milisekundy, podpora pro získání počtu snímků vykreslených za sekundu (FPS – frames per second)
love.system získání základních informací o systému LÖVE a taktéž o použité platformě (Windows, Linux atd.)

3. Nastavení grafického režimu, základy práce s 2D grafikou

V této kapitole si stručně popíšeme některé funkce dostupné z modulu love.graphics. Před použitím funkcí pro vykreslování je nejprve vhodné buď vytvořit okno se zadanou velikostí, do nějž bude vykreslování prováděno nebo nastavit celoobrazovkový grafický režim – fullscreen mode – ke kterému bude mít aplikace exkluzivní přístup, což znamená, že se pro celoobrazovkový režim alokuje část video paměti, do které bude mít přístup pouze jedna aplikace a při porušení této paměti systém sám požádá o překreslení celé scény. Okno či celoobrazovkový grafický režim lze nastavit funkcí love.graphics­.setMode(), ovšem před voláním této funkce je vhodné pomocí love.graphics­.getModes() nebo love.graphics­.checkMode() zjistit, zda je daný grafický režim podporován (například na mnoha počítačích existují poměrně restriktivní omezení pro rozlišení celoobrazovkových režimů). Funkce love.graphics­.getModes() vrátí seznam dostupných celoobrazovkových grafických režimů, zatímco funkce love.graphics­.checkMode() zjistí, zda je možné použít grafický režim se zadaným rozlišením.

Při vytváření okna nebo přepnutí do celoobrazovkového režimu lze nastavit požadované horizontální a vertikální rozlišení, dále zvolit, zda se má při vykreslování čekat na vertikální synchronizaci (na CRT monitorech se jedná o návrat paprsku do levého horního rohu obrazovky) a taktéž globálně povolit či zakázat antialiasing hran vykreslovaných obrazců (interně se pro nastavení antialiasu využívají funkce grafické knihovny OpenGL, která představuje standardizované aplikační rozhraní ke grafickým akcelerátorům). Po nastavení grafického režimu je již možné začít pracovat s rastrovými obrázky, které mohou být na disku uložené v různých souborových formátech (o práci s grafickými formáty se interně stará knihovna DevIL a zlib), pracovat s animacemi vytvořenými buď programově či načtenými z disku, vykreslovat znaky pomocí rastrových fontů (uložených v externím obrázku) či fontů typu TTF (zde je využita knihovna FreeType), vykreslovat základní geometrické tvary typu bod, úsečka, kružnice, trojúhelník či obecný polygon, nebo dokonce vykreslit tak složitý objekt, jako je částicový systém – viz navazující část tohoto seriálu.

Následuje krátký demonstrační příklad, na kterém je ukázán způsob načtení rastrového obrázku z externího souboru pomocí funkce love.graphics­.newImage() s jeho následným zobrazením, které využívá „přetíženou“ funkci love.graphics­.draw() (chování této funkce se liší podle, toho, jaké objekty jsou jí předány pro vykreslení; zde se jedná o objekt typu Image). V níže vypsaném skriptu jsou nadefinovány pouze dvě funkce nazvané draw a load. Jména těchto funkcí nebyla zvolená náhodně, jelikož funkce draw je automaticky zavolána při překreslení okna nebo celé obrazovky a funkce load při inicializaci aplikace (hry) – typicky se v ní provádí načtení konfiguračních souborů, obrázků, fontů či zvuků a hudby. Tento demonstrační příklad lze najít i v dokumentaci k systému LÖVE, viz odkazy uvedené v deváté kapitole na konci článku:

function load()
    image = love.graphics.newImage("images/love-ball.png")
end

function draw()
    love.graphics.draw(image, 400, 300)
end 

4. Podpora přehrávání zvuků a hudby

Pro práci se zvuky a hudbou lze využít modul love.audio, který je interně založen na knihovně SDL_mixer. Modul love.audio rozlišuje dva typy objektů: zvuk (Sound) a hudbu (Music). Objekt typu zvuk může být na disku či jiném obdobném paměťovém médiu uložen ve formátech WAV, OGG, VOC, AIFF či RIFF. Při načítání těchto souborů, které je většinou prováděno v callback funkci load, se zvuk dekomprimuje (případ formátu OGG) a zkonvertuje tak, aby ho bylo možné v libovolný okamžik ihned přehrát. Tento způsob sice vyžaduje alokaci většího bloku operační paměti, ovšem před začátkem přehrávání zvuku nedochází k nežádoucím zpožděním (například při vystřelení ve hře by se zvuk výstřelu neměl opozdit za animací). Naproti tomu objekty typu hudba (Music) se z externích souborů načítají a dekódují postupně, tj. spotřeba paměti je nižší a díky využití vyrovnávacích pamětí (většinou) nedochází k přerušení přehrávání, ovšem samotný začátek přehrávání se může poněkud opozdit (což většinou není kritické). Hudbu je možné mít uloženou v mnoha formátech, včetně MP3, OGG, MIDI, MOD, XM a několika dalších formátech používaných hudebními trackery. Praktický příklad použití hudby a zvuků bude ukázán v následující části tohoto seriálu.

5. Použití časovače – základ interaktivních her a animací

Modul love.timer je možné použít pro změření času mezi dvěma po sobě jdoucími snímky. Jedná se o hodnotu známou pod zkratkou FPS, neboli frames per second. Rychlost samotné hry by totiž měla být nezávislá na hodnotě FPS, což znamená, že na pomalejším počítači (nebo na počítači s pomalejší grafickou kartou) se sice vykreslí menší počet snímků, ovšem „skoky“ mezi snímky budou větší, aby se na úkor méně kvalitní animace kompenzoval nižší výpočetní výkon. Pokud například programátor hry vyžaduje, aby se nějaký objekt za jednu sekundu přesunul z levého okraje obrazovky na okraj pravý, může být na pomalejším počítači vykresleno pouze 5 snímků (a skok mezi pozicemi objektu tak může dosahovat například 200 pixelů), zatímco na počítači rychlejším se vykreslí snímků 50 a jednotlivé pozice objektu v těchto snímcích se budou lišit o 20 pixelů. Přesnost použitého časovače v současné verzi systému LÖVE dosahuje na podporovaných platformách 1 ms, v budoucích verzích se však očekává, že se využijí mnohem přesnější časovače zabudované přímo v mikroprocesorech (moderní mikroprocesory dokonce dokážou počítat s přesností na jeden takt interních hodin, což řádově odpovídá desetinám nanosekund).

6. Čtení stavu klávesnice

Pro čtení stavu stlačených kláves, ovládání myši a joysticků lze použít moduly love.keyboard, love.mouse a love.joystick. Modul love.keyboard obsahuje jedinou funkci love.keyboard­.isDown(), pomocí níž lze zjistit, která klávesa či klávesy jsou v daném okamžiku stisknuty. Jedná se o práci s klávesnicí na nejnižší úrovni, tj. lze například rozeznat stavy přeřaďovačů (Shift, Alt, Ctrl), odlišit numerický blok klávesnice od kurzorového bloku i numerických kláves umístěných v alfanumerickém bloku atd. Tuto funkci je možné použít i v případě, že je současně stlačeno větší množství kláves a klávesnice tuto kombinaci rozezná, což však nemusí být – vzhledem k architektuře PC klávesnic – vždy splněno. Alternativní způsob čtení stavu klávesnice spočívá ve využití callback funkcí nazvaných keypressed() a keyreleased(), které jsou automaticky zavolány vždy při stisku či naopak uvolnění klávesy. Kódy jednotlivých kláves jsou uloženy ve formě konstant přímo v asociativním poli love. Každá konstanta představující kód klávesy začíná prefixem key_, tj. například klávesa Backspace je ve všech třech výše uvedených funkcích reprezentována konstantou love.key_backspa­ce, pravý Shift konstantou love.key_rshift atd. Podrobnosti si uvedeme příště.

7. Práce s myší

Pro ovládání myši i zjištění aktuální pozice kurzoru a stavu všech tlačítek myši lze využít modul nazvaný love.mouse, jenž obsahuje funkci love.mouse.set­Visible() pro skrytí či naopak zviditelnění kurzoru myši, funkce love.mouse.get­X(), love.mouse.getY() a love.mouse.get­Position() pro přečtení aktuálních souřadnic kurzoru, funkci love.mouse.is­Down() pro zjištění, které tlačítko či tlačítka myši jsou v daném okamžiku stlačena a konečně funkci setPosition() pomocí níž lze změnit souřadnice kurzoru myši a tím i pozici kurzoru na obrazovce. Kromě výše zmíněných funkcí je možné vytvořit dvě uživatelské callback funkce nazvané mousepressed() a mousereleased(). Tyto funkce jsou systémem automaticky zavolány v případě, že je stisknuto či naopak uvolněno některé tlačítko myši. Vždy jsou naplněny tři parametry – aktuální x-ová souřadnice kurzoru myši, y-ová souřadnice a jedna z konstant love.mouse_left, love.mouse_middle či love.mouse_right podle toho, které tlačítko myši ji stisknuto či uvolněno.

8. Práce s joysticky

Posledním modulem systému LÖVE, který si v dnešní části seriálu o jazyku Lua stručně představíme, je modul nazvaný love.joystick, jenž obsahuje funkce pro zjištění stavu joysticku či dalších podobných vstupních zařízení, například volantů apod. Pomocí funkce love.joystick­.getNumJoystic­ks() lze zjistit celkový počet nainstalovaných joysticků, funkcí love.joystick­.getName() se přečte jméno n-tého joysticku (jména lze využít například v konfiguračních dialozích vytvářené hry), funkce love.joystick­.getNumAxes() a love.joystick­.getNumBalls() slouží pro zjištění počtu os (otáčení/naklánění) vybraného joysticku popř. počtu trackballů, v závislosti na tom, které ovládací prvky joystick skutečně obsahuje.

bitcoin_skoleni

Další funkce se již používají přímo v průběhu hry pro získání aktuálního stavu vybraného joysticku: love.joystick­.isDown() pro přečtení stavu některého tlačítka joysticku, love.joystick­.getAxis() pro přečtení náklonu vybrané osy či love.joystick­.getAxes() pro současné přečtení náklonu všech os joysticku. Podobně jako v případě klávesnice a myši, i pro automatickou reakci na změnu stavu tlačítek joysticku lze vytvořit callback funkce zavolané tehdy, pokud je stlačeno či naopak uvolněno některé tlačítko joysticku. Těmto globálním funkcím, jenž se musí jmenovat joystickpressed() a joystickrelea­sed(), je předána konstanta určující jméno stlačeného či naopak uvolněného tlačítka.

9. Odkazy na Internetu

  1. Domovská stránka systému LÖVE
    http://love2d­.org/
  2. Tutoriály k systému LÖVE
    http://love2d­.org/?page=do­cumentation
  3. Screenshoty aplikací vytvořených v LÖVE
    http://love2d­.org/screenshots
  4. Domovská stránka programovacího jazyka Lua
    http://www.lu­a.org/
  5. Lua
    http://www.li­nuxexpres.cz/pra­xe/lua
  6. Lua
    http://cs.wiki­pedia.org/wiki/Lua
  7. Lua (programming language)
    http://en.wiki­pedia.org/wiki/Lu­a_(programmin­g_language)
  8. The Lua Programming Language
    http://www.ti­obe.com/index­.php/paperinfo/tpci/Lu­a.html
  9. Lua Programming Gems
    http://www.lu­a.org/gems/
  10. LuaForge
    http://luafor­ge.net/
  11. Forge project tree
    http://luafor­ge.net/softwa­remap/trove_lis­t.php
  12. gamedev.net: Lua
    http://www.ga­medev.net/refe­rence/program­ming/features/lu­a/
  13. Category:Lua-scripted games
    http://en.wiki­pedia.org/wiki/Ca­tegory:Lua-scripted_games
  14. Category:Lua-scriptable games
    http://en.wiki­pedia.org/wiki/Ca­tegory:Lua-scriptable_games
  15. BZFlag
    http://en.wiki­pedia.org/wiki/BZFlag
  16. BZFlag.org
    http://bzflag­.org/
  17. GrimE
    http://en.wiki­pedia.org/wiki/Gri­mE
  18. Grim Fandango
    http://www.mo­bygames.com/ga­me/grim-fandango
  19. Escape from Monkey Island
    http://www.mo­bygames.com/ga­me/escape-from-monkey-island

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.