Ve čtvrtém článku věnovaném programovacím jazykům a taktéž knihovnám, které mohou být vhodné pro výuku programování i základů počítačové grafiky, se opět budeme zabývat popisem možností knihovny LÖVE, o níž jsme se stručně zmínili minule i předminule. Na demonstračních příkladech si ukážeme použití 2D grafiky, tvorbu animací i práci se sprity.

Obsah

1. Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky (4)

2. Šestý demonstrační příklad: kreslení s využitím funkcí z modulu love.graphics

3. Nastavení parametrů vykreslování

4. Sedmý demonstrační příklad: kreslení s využitím funkcí z modulu love.graphics, směšování barev

5. Rastrové obrázky a sprity aneb základní objekty při tvorbě 2D her

6. Osmý demonstrační příklad: základy práce se sprity

7. Tvorba animací s využitím callback funkce nazvané love.update()

8. Devátý demonstrační příklad: pohyb spritů na obrazovce (skákající míčky)

9. Otáčení spritů okolo jejich lokální souřadnice [0,0]

10. Desátý demonstrační příklad: otáčení spritů při vykreslování

11. Další parametry funkce love.draw()

12. Jedenáctý demonstrační příklad: korektní rotace spritů

13. Repositář se zdrojovými kódy dnešních demonstračních příkladů

14. Odkazy na Internetu

1. Programovací jazyky a knihovny určené pro výuku základů počítačové grafiky (4)

V předchozí části seriálu o programovacích jazycích a taktéž knihovnách, které mohou být vhodné pro výuku programování i základů počítačové grafiky, jsme se seznámili s některými možnostmi nabízenými knihovnou LÖVE. Víme již, že tato knihovna je určena především pro jednoduchou a rychlou tvorbu her s 2D grafikou, hudbou a zvuky, přičemž pro zápis algoritmů se používá snadno naučitelný programovací jazyk Lua. Samozřejmě je možné LÖVE použít i pro tvorbu jiných typů aplikací, například různých dem, simulací s grafickým výstupem atd. (pro složitější aplikace však chybí funkce pro tvorbu plnohodnotného grafického uživatelského rozhraní). Minule jsme si na pěti demonstračních příkladech ukázali některé základní grafické (vykreslovací) funkce a taktéž způsob obsluhy klávesnice a myši. Dnes se zaměříme na popis dalších možností nabízených především subsystémem pro práci s grafikou, tj. na funkce, které lze nalézt v modulu love.graphics.

03

Obrázek 1: Ukázka hry naprogramované s využitím knihovny LÖVE.

V následující tabulce jsou pro přehlednost uvedeny ty funkce knihovny LÖVE, které budou popsány v dalších kapitolách:

# Funkce Kapitola
1 love.graphics.setMode() 2
2 love.window.setMode() 2
3 love.graphics.newFont() 2
4 love.graphics.setFont() 2
5 love.graphics.print() 2
6 love.graphics.line() 2
7 love.graphics.setColor() 3
8 love.graphics.setColorMask() 3
9 love.graphics.setColorMode() 3
10 love.graphics.setBlendMode() 3
11 love.graphics.setLineWidth() 3
12 love.graphics.setLineStyle() 3
13 love.graphics.setLineStipple() 3
14 love.graphics.setPointSize 3
15 love.graphics.setPointStyle 3
16 love.graphics.newImage 5
17 love.graphics.draw 5

04

Obrázek 2: Prohlížeč demonstračních příkladů.

2. Šestý demonstrační příklad: kreslení s využitím funkcí z modulu love.graphics

Minule jsme si ukázali, jakým způsobem je možné vykreslit základní grafický obrazec (obdélník) i způsob načtení a následného zobrazení rastrových obrázků. V dalším demonstračním příkladu, který je v pořadí už šestým příkladem, je použito hned několik nových funkcí. První funkce slouží pro nastavení grafického režimu a popř. i pro nastavení velikosti hlavního okna aplikace; tato funkce se tedy může používat v handleru love.load() (ten je zavolán při inicializaci aplikace).

Funkce pro nastavení grafického režimu ve skutečnosti existuje ve dvou variantách v závislosti na tom, která verze knihovny LÖVE je použita. Ve verzi 0.8 se používá funkce nazvaná love.graphics.setMode(), ve verzi 0.9 pak funkce nazvaná love.window.setMode(). I parametry obou zmíněných funkcí se odlišují, nicméně v obou případech se do prvních dvou parametrů zadává velikost hlavního okna aplikace. U funkce love.graphics.setMode() se dále zadává trojice hodnot: zda se má využít celoobrazovkový režim (fullscreen), zda se má při vykreslování čekat na vertikální zatemnění (v-sync) a zda se má použít buffer pro antialiasing. U funkce love.window.setMode() jsou tyto parametry předány v tabulce předané jako třetí parametr (v demonstračním příkladu je tato tabulka prázdná).

Další trojice funkcí, které jsou v tomto demonstračním příkladu použity, slouží pro práci s textem. Jedná se o funkci love.graphics.newFont() určenou pro načtení fontu (my zde používáme výchozí font nabízený knihovnou LÖVE). Funkce love.graphics.setFont() umožňuje vybrat si aktuálně používaný font v případě, že jich je načteno větší množství. A konečně funkce love.graphics.print() vytiskne zadaný řetězec na určené souřadnice na obrazovce. Poslední novou funkcí, která je v příkladu použita, je funkce nazvaná love.graphics.line(). Ta ve své nejjednodušší podobě slouží pro vykreslení úsečky z bodu [x1, y1] do bodu [x2, y2] aktuálně nastavenou barvou.

03

Obrázek 3: Screenshot v pořadí šestého demonstračního příkladu.

Všechny zmíněné funkce naleznete ve zdrojovém kódu šestého demonstračního příkladu:

-- 
--  Knihovna LÖVE
-- 
--  Šestý demonstrační příklad
-- 
--  Kreslení s využitím funkcí z knihovny love.graphics
-- 
 
 
 
--  rozměry okna
width = 450
height = 450
 
--  poloměr vykreslovaného obrazce
radius = 200
 
 
 
-- 
--  Funkce volaná při inicializaci aplikace.
-- 
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 20)
    love.window.setMode(width, height, {})
    love.graphics.setFont(font)
end
 
 
 
-- 
--  Tato funkce je volána automaticky při překreslení obsahu
--  okna či obrazovky.
-- 
function love.draw()
    local max = 120
    -- vykreslení obrazce složeného ze sady rovnostranných trojúhelníků
    for i=1, max, 3 do
        -- natočení trojúhelníku
        local colorAngle = math.rad(i*360/max)
        -- výpočet a nastavení barvy
        love.graphics.setColor(128+127*math.cos(colorAngle), 255, 128+128*math.sin(colorAngle))
        for j=0, 2 do
            local angle1 = math.rad(i + j * 120)
            local angle2 = angle1 + math.rad(120)
            local x1 = width/2 + radius*math.cos(angle1)
            local y1 = width/2 + radius*math.sin(angle1)
            local x2 = width/2 + radius*math.cos(angle2)
            local y2 = width/2 + radius*math.sin(angle2)
            love.graphics.line(x1, y1, x2, y2)
        end
    end
    love.graphics.setColor(255, 200, 255, 255)
    love.graphics.print("Press escape to exit.", 30, 433)
end
 
 
 
-- 
--  Callback funkce zavolaná při stisku klávesy.
-- 
function love.keypressed(k)
    if k == 'escape' then
        love.event.quit()
    end
end
 
-- 
--  finito
-- 

Pokud používáte knihovnu LÖVE verze 0.9, je nutné změnit funkci love.load() následujícím způsobem:

--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 20)
    love.window.setMode(width, height, {})
    love.graphics.setFont(font)
end

04

Obrázek 4: Chybové hlášení vypsané v případě, že se použije nekompatibilní verze knihovny LÖVE.

3. Nastavení parametrů vykreslování

Knihovna love.graphics obsahuje několik funkcí určených pro vykreslování základních geometrických obrazců, především bodů (love.graphics.point), úseček (love.graphics.line), trojúhelníků (love.graphics.triangle(), obdélníků (love.graphics.rectangle) a kružnic (love.graphics.circle). Tyto obrazce jsou vykresleny nastavenou barvou, tloušťkou čáry, stylem čáry (plná, čárkovaná atd.) a se zapnutým či vypnutým antialiasingem. Povolení antialiasingu funkcí love.graphics.setLineStyle(Linestyle.rough/smooth) poměrně výrazným způsobem zlepšuje kvalitu výsledného obrázku, především u šikmých linek, kde kvůli vlastnostem rastrového displeje dochází k tvorbě „schodů“ – jagged lines, ovšem toto vizuální vylepšení jde na úkor rychlosti vykreslování. U jednodušších her, ve kterých se používá omezený počet objektů (řádově stovky), si však s tímto typem antialiasingu soudobé grafické akcelerátory bez větších problémů poradí, protože vykreslování je prováděno (přes knihovnu OpenGL i ovladač grafické karty) samotným GPU umístěným na akcelerátoru. Seznam nejdůležitějších funkcí ovlivňujících způsob vykreslování, je uveden v tabulce níže:

# Název funkce Význam
1 love.graphics.setColor() nastavení barvy vykreslovaných objektů
2 love.graphics.setColorMask() povoluje či zakazuje jednotlivé barvové kanály (přidáno ve verzi 0.9)
3 love.graphics.setColorMode() určení, zda se má při vykreslování provádět modulace (násobení) barvy s pozadím (odstraněno ve verzi 0.9!)
4 love.graphics.setBlendMode() povolení či zákaz míchání barev s pozadím při vykreslování
5 love.graphics.setLineWidth() šířka čáry při vykreslování
6 love.graphics.setLineStyle() povolení či zákaz antialiasingu při vykreslování úseček
7 love.graphics.setLineStipple() nastavení typu čáry podle zadaného bitového vzorku (odstraněno ve verzi 0.9!)
8 love.graphics.setPointSize velikost vykreslovaných bodů
9 love.graphics.setPointStyle styl vykreslovaných bodů (bez či s antialisingem)

4. Sedmý demonstrační příklad: kreslení s využitím funkcí z modulu love.graphics, směšování barev

V sedmém demonstračním příkladu, jehož zdrojový kód je vypsán pod tímto odstavcem, je ukázáno použití funkce love.graphics.setBlendMode() i význam čtvrtého (nepovinného) parametru funkce love.graphics.setColor(), nazývaného alfa (alpha). Tento parametr určuje průhlednost nastavené barvy a tím i celkový vizuální vzhled objektu. Po spuštění demonstračního příkladu je možné pomocí klávesy B přepínat režim míchání barev mezi "alpha" a "additive". V závislosti na vybraném režimu je buď nastavená barva pouze vynásobena hodnotou alfa (předaná hodnota je nejprve vydělena 255, aby se nacházela v rozsahu 0.0 až 1.0), ovšem ve druhém případě je proveden součet barvy pozadí (tj. i dříve nakreslených objektů) s barvou vykreslovaného objektu, takže v místech, kde se objekty překrývají, dochází ke zvýšení intenzity barev, což ostatně můžeme vidět dvojici screenshotů:

05

Obrázek 5: Screenshot sedmého demonstračního příkladu.

06

Obrázek 6: Screenshot sedmého demonstračního příkladu (změna režimu směšování barev).

-- 
--  Knihovna LÖVE
-- 
--  Sedmý demonstrační příklad
-- 
--  Kreslení s využitím funkcí z knihovny love.graphics, nastavení režimu
--  směšování barev
-- 
 
 
--  rozměry okna
width = 450
height = 450
 
--  poloměr vykreslovaného obrazce
radius = 200
 
--  režim směšování barev
blending = "alpha"
--  konstanta pro směšování
blend_factor = 80
 
 
 
-- 
--  Funkce volaná při inicializaci aplikace.
-- 
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 20)
    love.graphics.setMode(width, height, false, false, 0)
    love.graphics.setFont(font)
end
 
 
 
-- 
--  Tato funkce je volána automaticky při překreslení obsahu
--  okna či obrazovky.
-- 
function love.draw()
    -- nastavení režimu směšování barev
    love.graphics.setBlendMode(blending)
    love.graphics.setColor(255, 255, 255, blend_factor)
    -- vykreslení obrazce
    for i=1, 90 do
        for j=0, 3 do
            local angle1 = math.rad(i + j * 90)
            local angle2 = angle1 + 90
            local x1 = width/2 + radius*math.cos(angle1*2)
            local y1 = width/2 + radius*math.sin(angle1*3)
            local x2 = width/2 + radius*math.cos(angle2*3)
            local y2 = width/2 + radius*math.sin(angle2*2)
            love.graphics.line(x1, y1, x2, y2)
        end
    end
    love.graphics.setColor(255, 200, 255, 255)
    love.graphics.print("Press escape to exit.", 30, 433)
end
 
 
 
-- 
--  Callback funkce zavolaná při stisku klávesy.
-- 
function love.keypressed(k)
    if k == 'escape' then
        love.event.quit()
    elseif k == 'b' then
        setBlending()
    end
end
 
--  povolení či zákaz směšování barev po stisku klávesy "b"
function setBlending()
    if blending == "alpha" then
        blending = "additive"
    else
        blending = "alpha"
    end
end
 
 
-- 
--  finito
-- 

Podobně jako u předchozího příkladu platí, že pokud používáte knihovnu LÖVE verze 0.9, je nutné změnit funkci love.load() následujícím způsobem:

--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 20)
    love.window.setMode(width, height, {})
    love.graphics.setFont(font)
end

5. Rastrové obrázky a sprity aneb základní objekty při tvorbě 2D her

Základní grafickou entitou používanou při tvorbě naprosté většiny klasických her s 2D grafikou, jsou rastrové obrázky, jejichž některé pixely jsou buď zcela průhledné či poloprůhledné. Díky měnitelné průhlednosti (definované až na úroveň jednotlivých pixelů) je možné takové rastrové obrázky použít pro zobrazení pohybujících se objektů či naopak statických prvků jednotlivých scén, které mohou překrývat pozadí. V dobách osmibitových domácích mikropočítačů a herních konzolí se takové bitmapy (s malým rozlišením a omezeným množstvím barev) označovaly jménem sprite a o jejich vykreslování se mnohdy staraly specializované čipy. Dnes je vlastně situace obdobná, protože bitmapy jsou (i s případnou průhledností) vykreslovány s využitím grafických akcelerátorů a jejich GPU. V následujících demonstračních příkladech si ukážeme použití moderní obdoby spritů s využitím funkcí nabízených knihovnou LÖVE.

07

Obrázek 7: Historická hra pro osmibitové mikropočítače (Draconus), v níž se pro zobrazení postavy ovládané hráčem používaly hardwarově vykreslované sprity, tj. bitmapy o malém rozlišení a omezeném počtu barev, o jejichž vykreslení se staral specializovaný čip.

6. Osmý demonstrační příklad: základy práce se sprity

V tomto demonstračním příkladu si připomeneme, jakým způsobem se rastrový obrázek dá načíst z externího souboru a jaká funkce posléze slouží k jeho zobrazení. Načtení obrázku je realizováno funkcí love.graphics.newImage(), která dokáže pracovat s prakticky všemi základními grafickými formáty, samozřejmě včetně dnes pravděpodobně nejpoužívanějších formátů PNG a JPEG. Při použití formátu PNG je plně podporována průhlednost jednotlivých pixelů, a to jak průhlednost reprezentovaná jedním bitem (pixel je buď plně průhledný či naopak zcela neprůhledný), tak i plnohodnotný alfa kanál. Podrobnosti o možnostech formátu PNG jsou popsány v článcích PNG is not GIF a PNG - bity, byty, chunky, nás však dnes bude zajímat především fakt, že právě PNG lze použít pro tvorbu plnohodnotných spritů. Obrázky s průhledností jsou totiž v osmém demonstračním příkladu použity pro vytvoření následující 2D scény:

08

Obrázek 8: Screenshot osmého demonstračního příkladu.

Následuje výpis zdrojového kódu osmého demonstračního příkladu:

--
-- Knihovna LÖVE
--
-- Osmý demonstrační příklad
--
-- Práce se sprity.
--
 
 
-- rozměry okna
width = 450
height = 450
 
 
 
local sprite1 = nil
local sprite2 = nil
 
 
 
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 40)
    -- načtení spritů
    sprite1 = love.graphics.newImage("sprite1.png")
    sprite2 = love.graphics.newImage("sprite2.png")
    love.graphics.setMode(width, height, false, false, 0)
    love.graphics.setFont(font)
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- velikost okna
    local windowWidth = love.graphics.getWidth()
    local windowHeight = love.graphics.getHeight()
 
    -- vykreslení mřížky
    for y=0, windowHeight - sprite2:getHeight(), sprite2:getHeight()+5 do
        for x=0, windowWidth - sprite2:getWidth(), sprite2:getWidth()+5 do
            love.graphics.draw(sprite2, x, y)
        end
    end
 
    -- vykreslení prvního spritu přes ostatní sprity
    love.graphics.draw(sprite1, 30, 30)
    love.graphics.print("Press escape to exit.", 30, 433)
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    if k == 'escape' then
        love.event.quit()
    end
end
 
 
 
--
-- finito
--

7. Tvorba animací s využitím callback funkce nazvané love.update()

Všechny předchozí příklady pouze zobrazovaly statickou 2D scénu, což vlastně ani není příliš zajímavé. Užitečnější je tvorba animací, ať již animací běžících zcela automaticky (různá grafická dema atd.), tak i dynamických scén, které může svou činností ovlivňovat uživatel (počítačové hry). V obou případech je nutné, aby existovala nějaká periodicky volaná funkce, v jejímž těle se mění stav programu a tím pádem i například pozice spritů na obrazovce. V knihovně LÖVE se tato funkce jmenuje love.update(). Tuto funkci automaticky volá engine knihovny LÖVE a uživatel může sám zjistit, kolik času uběhlo od předchozího volání této funkce přečtením jejího parametru dt (tento čas je udaný v sekundách, ovšem jedná se o číslo s plovoucí řádovou čárkou, takže časový úsek je možné měřit s větší přesností). Jedna z možností, jak zajistit změnu stavu programu v pravidelných intervalech (zde konkrétně v intervalu jedné třicetiny sekundy) spočívá v použití funkce love.timer.sleep(), která pozastaví provádění celého programu po nastavený časový okamžik. Nepříliš přesné, ovšem pro nás dostačující, řešení může vypadat následovně:

--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    -- změna stavu aplikace
    -- změna stavu aplikace
    -- změna stavu aplikace
    local delay = 1/30
    if dt < delay then
        love.timer.sleep(delay - dt)
    end
end

8. Devátý demonstrační příklad: pohyb spritů na obrazovce (skákající míčky)

Funkce love.update(), která byla zmíněná v předchozí kapitole, je použita v devátém demonstračním příkladu pro animaci třech skákajících míčků představovaných ve vykreslované scéně sprity. Stav celého programu je uložen v proměnných sprite1, sprite2 a sprite3, což jsou asociativní pole obsahující jak vlastní sprite (obrázek s poloprůhlednými i průhlednými pixely), tak i jeho aktuální pozici na obrazovce a vektor pohybu (proměnné dx, dy, zjednodušeně tento vektor můžeme považovat za rychlost). Vertikální složka vektoru pohybu se v čase postupně mění, čímž je simulován vliv gravitace. Odrážení míčků od stěn je jednoduché – pouze se změní znaménko horizontální či vertikální složky vektoru pohybu. Animaci a současně i celý program je možné zastavit klávesou Esc. Animace vytvořená tímto demonstračním programem vypadá následovně:

anim1_

Zdrojový kód devátého demonstračního příkladu:

--
-- Knihovna LÖVE
--
-- Devátý demonstrační příklad
--
-- Práce se sprity: zobrazení, posun.
-- (skákající míčky)
--
 
 
-- rozměry okna
width = 450
height = 450
 
 
 
-- objekty představující sprity zobrazené v okně
local sprite1 = {x=100, y=100, dx=4, dy=0}
local sprite2 = {x=200, y=10,  dx=3, dy=0}
local sprite3 = {x=300, y=100, dx=2, dy=0}
 
 
 
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 40)
    -- načtení spritů
    sprite1.image = love.graphics.newImage("sprite2.png")
    sprite2.image = love.graphics.newImage("sprite1.png")
    sprite3.image = love.graphics.newImage("sprite2.png")
    love.graphics.setMode(width, height, false, false, 0)
    -- nastavení fontu
    love.graphics.setFont(font)
end
 
 
 
--
-- Vykreslení spritu s jeho posunem.
--
function drawSprite(sprite)
    -- vykreslení spritu s posunem
    love.graphics.draw(sprite.image, sprite.x, sprite.y)
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- vykreslení prvního spritu přes ostatní sprity
    drawSprite(sprite1)
    -- vykreslení druhého spritu přes ostatní sprity
    drawSprite(sprite2)
    -- vykreslení třetího spritu přes ostatní sprity
    drawSprite(sprite3)
 
    love.graphics.print("Press escape to exit.", 30, 433)
end
 
 
 
--
-- Změna pozice spritů v okně
--
function updateSpritePosition(sprite)
    -- velikost okna
    local windowWidth = love.graphics.getWidth()
    local windowHeight = love.graphics.getHeight()
 
    sprite.x = sprite.x + sprite.dx
    sprite.y = sprite.y + sprite.dy
 
    if sprite.x < 1 or sprite.x > windowWidth - sprite.image:getWidth() - 2 then
        sprite.dx = -sprite.dx
    end
    if sprite.y < 1 or sprite.y > windowHeight - sprite.image:getHeight() - 2 then
        sprite.dy = -sprite.dy
    end
    sprite.dy = sprite.dy + 0.1
end
 
 
 
--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    updateSpritePosition(sprite1)
    updateSpritePosition(sprite2)
    updateSpritePosition(sprite3)
    local delay = 1/30
    if dt < delay then
        love.timer.sleep(delay - dt)
    end
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    if k == 'escape' then
        love.event.quit()
    end
end
 
 
 
--
-- finito
--

09

Obrázek 9: Screenshot devátého demonstračního příkladu.

9. Otáčení spritů okolo jejich lokální souřadnice [0,0]

Funkce love.graphics.draw() používaná mj. i pro vykreslení obrázků do 2D scény, ve skutečnosti akceptuje více parametrů, než jen proměnnou obsahující obrázek a x-ovou i y-ovou pozici obrázku na obrazovce. Čtvrtým parametrem této funkce je možné specifikovat otočení obrázku, což se samozřejmě může v mnoha případech hodit (představit si můžeme například vesmírnou střílečku typu Asteroids či závodní hru, kde jsou autíčka viděna svrchu). Při rotaci obrázků v průběhu jejich vykreslování je však zapotřebí dbát na dvě vlastnosti funkce love.graphics.draw(). Rotace je udávána v radiánech, takže pokud se ve hře úhel reprezentuje ve stupních, je nutné zajistit převod této hodnoty, například vynásobením konstantou 180/π. Druhá vlastnost může přinášet jisté komplikace – otáčení obrázku je totiž provedeno okolo jeho počátku, tj. okolo souřadnic [0,0] a nikoli okolo středu obrázku. Výsledek tedy nemusí ve všech případech odpovídat představám vývojáře, o čemž se přesvědčíme hned v dalším demonstračním příkladu.

10. Desátý demonstrační příklad: otáčení spritů při vykreslování

V desátém demonstračním příkladu je ukázáno použití funkce love.graphics.draw() pro vykreslení otočených obrázků. Opět se jedná o animaci, přičemž se ve funkci love.update() mění pouze proměnná sprite.r představující úhel otočení daného spritu reprezentovaný v radiánech. O vykreslení spritů se stará uživatelská funkce pojmenovaná drawSprite(), jejíž zdrojový kód vypadá následovně:

--
-- Vykreslení spritu s jeho otočením.
--
function drawSprite(sprite)
    -- vykreslení spritu s rotací
    love.graphics.draw(sprite.image, sprite.x, sprite.y, sprite.r)
end

Vzhledem k tomu, že sprity jsou otáčeny okolo jejich lokální souřadnice [0,0], nebude pravděpodobně výsledná animace odpovídat představám vývojáře, o čemž se ostatně můžeme snadno přesvědčit:

anim2_

Zdrojový kód desátého demonstračního příkladu:

--
-- Knihovna LÖVE
--
-- Desátý demonstrační příklad
--
-- Práce se sprity: zobrazení, otočení.
--
 
 
-- rozměry okna
width = 450
height = 450
 
 
 
-- objekty představující sprity zobrazené v okně
local sprite1 = {x=100, y=100, r=0, dr=-0.17}
local sprite2 = {x=185, y=80, r=0, dr=0.1}
local sprite3 = {x=300, y=100, r=0, dr=0.05}
 
 
 
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 40)
    -- načtení spritů
    sprite1.image = love.graphics.newImage("sprite2.png")
    sprite2.image = love.graphics.newImage("sprite1.png")
    sprite3.image = love.graphics.newImage("sprite2.png")
    love.graphics.setMode(width, height, false, false, 0)
    -- nastavení fontu
    love.graphics.setFont(font)
end
 
 
 
--
-- Vykreslení spritu s jeho otočením.
--
function drawSprite(sprite)
    -- vykreslení spritu s rotací
    love.graphics.draw(sprite.image, sprite.x, sprite.y, sprite.r)
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- vykreslení prvního spritu přes ostatní sprity
    drawSprite(sprite1)
    -- vykreslení druhého spritu přes ostatní sprity
    drawSprite(sprite2)
    -- vykreslení třetího spritu přes ostatní sprity
    drawSprite(sprite3)
 
    love.graphics.print("Press escape to exit.", 30, 433)
end
 
 
 
--
-- Změna rotace spritů v okně
--
function updateSpriteAngle(sprite)
    sprite.r = sprite.r + sprite.dr
end
 
 
 
--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    updateSpriteAngle(sprite1)
    updateSpriteAngle(sprite2)
    updateSpriteAngle(sprite3)
    local delay = 1/30
    if dt > delay then
        love.timer.sleep(delay - dt)
    end
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    if k == 'escape' then
        love.event.quit()
    end
end
 
 
 
--
-- finito
--

10

Obrázek 10: Screenshot desátého demonstračního příkladu: začátek animace.

11

Obrázek 11: Screenshot desátého demonstračního příkladu: průběh animace.

11. Další parametry funkce love.draw()

Problém s rotací obrázků, se kterým jsme se setkali v předchozím demonstračním příkladu, je možné řešit dvěma způsoby: změnou souřadného systému (to si popíšeme příště) popř. použitím dalších parametrů funkce love.graphics.draw. Tato funkce totiž ve skutečnosti akceptuje až deset parametrů s následujícím významem:

# Parametr Význam
1 drawable obrázek či jiný objekt, který se má vykreslit
2 x horizontální souřadnice obrázku na obrazovce
3 y vertikální souřadnice obrázku na obrazovce
4 r otočení obrázku (radiány)
5 sx horizontální měřítko obrázku (1=beze změny velikosti)
6 sy vertikální měřítko obrázku (1=beze změny velikosti)
7 ox horizontální offset (ten se používá při otáčení)
8 oy vertikální offset
9 kx horizontální zešikmení
10 ky vertikální zešikmení

Pro rotaci obrázku okolo jakéhokoli bodu tedy postačuje vhodným způsobem specifikovat horizontální a vertikální offset. Konkrétně pro rotaci okolo středu obrázku:

--
-- Vykreslení spritu s jeho otočením.
--
function drawSprite(sprite)
    -- vykreslení spritu
    love.graphics.draw(sprite.image,
                       sprite.x, sprite.y, -- souřadnice na obrazovce (umístění spritu)
                       sprite.r,           -- rotace
                       1.0, 1.0,           -- zvětšení/změna měřítka
                       sprite.image:getWidth()/2, sprite.image:getHeight()/2) -- posun pozice středu otáčení
end

12. Jedenáctý demonstrační příklad: korektní rotace spritů

Upravená funkce drawSprite() zmíněná v předchozí kapitole, která otáčí každý obrázek/sprite okolo jeho středu, je součástí jedenáctého a současně i posledního demonstračního příkladu. Po jeho spuštění se zobrazí tato animace:

anim3_

Zdrojový kód jedenáctého demonstračního příkladu:

--
-- Knihovna LÖVE
--
-- Jedenáctý demonstrační příklad
--
-- Práce se sprity: zobrazení, otočení (korektní rotace).
--
 
 
-- rozměry okna
width = 450
height = 450
 
 
 
-- objekty představující sprity zobrazené v okně
local sprite1 = {x=100, y=100, r=0, dr=-0.17}
local sprite2 = {x=185, y=80,  r=0, dr=0.1}
local sprite3 = {x=300, y=100, r=0, dr=0.05}
 
 
 
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- načtení standardního fontu a nastavení grafického režimu
    local font = love.graphics.newFont(love.default_font, 40)
    -- načtení spritů
    sprite1.image = love.graphics.newImage("sprite2.png")
    sprite2.image = love.graphics.newImage("sprite1.png")
    sprite3.image = love.graphics.newImage("sprite2.png")
    love.graphics.setMode(width, height, false, false, 0)
    -- nastavení fontu
    love.graphics.setFont(font)
end
 
 
 
--
-- Vykreslení spritu s jeho otočením.
--
function drawSprite(sprite)
    -- vykreslení spritu
    love.graphics.draw(sprite.image,
                       sprite.x, sprite.y, -- souřadnice na obrazovce (umístění spritu)
                       sprite.r,           -- rotace
                       1.0, 1.0,           -- zvětšení
                       sprite.image:getWidth()/2, sprite.image:getHeight()/2) -- posun pozice středu otáčení
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- vykreslení prvního spritu přes ostatní sprity
    drawSprite(sprite1)
    -- vykreslení druhého spritu přes ostatní sprity
    drawSprite(sprite2)
    -- vykreslení třetího spritu přes ostatní sprity
    drawSprite(sprite3)
 
    love.graphics.print("Press escape to exit.", 30, 433)
end
 
 
 
--
-- Změna rotace spritů v okně
--
function updateSpriteAngle(sprite)
    sprite.r = sprite.r + sprite.dr
end
 
 
 
--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    updateSpriteAngle(sprite1)
    updateSpriteAngle(sprite2)
    updateSpriteAngle(sprite3)
    local delay = 1/30
    if dt > delay then
        love.timer.sleep(delay - dt)
    end
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    if k == 'escape' then
        love.event.quit()
    end
end
 
 
 
--
-- finito
--

12

Obrázek 12: Screenshot jedenáctého demonstračního příkladu: začátek animace.

13

Obrázek 13: Screenshot jedenáctého demonstračního příkladu: průběh animace.

14

Obrázek 14: Screenshot jedenáctého demonstračního příkladu: průběh animace.

13. Repositář se zdrojovými kódy dnešních demonstračních příkladů

Všech šest demonstračních příkladů, s nimiž jsme se v dnešním článku seznámili, bylo uloženo do Git repositáře na GitHubu (https://github.com/tisnik/presentations):

# Příklad Zdrojový kód
1 example06 pro v0.8 https://github.com/tisnik/presentations/tree/master/love/example06
2 example06 pro v0.9 https://github.com/tisnik/presentations/tree/master/love/example06_v09
3 example07 pro v0.8 https://github.com/tisnik/presentations/tree/master/love/example07
4 example07 pro v0.9 https://github.com/tisnik/presentations/tree/master/love/example07_v09
5 example08 https://github.com/tisnik/presentations/tree/master/love/example08
6 example09 https://github.com/tisnik/presentations/tree/master/love/example09
7 example10 https://github.com/tisnik/presentations/tree/master/love/example10
8 example11 https://github.com/tisnik/presentations/tree/master/love/example11

14. Odkazy na Internetu

  1. Domovská stránka systému LÖVE
    http://love2d.org/
  2. Dokumentace k systému LÖVE
    http://love2d.org/wiki/love
  3. Domovská stránka programovacího jazyka Lua
    http://www.lua.org/
  4. Seriál o programovacím jazyku Lua (root.cz):
    http://www.root.cz/serialy/programovaci-jazyk-lua/
  5. Domovská stránka systému LÖVE
    http://love2d.org/
  6. Domovská stránka programovacího jazyka Lua
    http://www.lua.org/
  7. Web o Lieru, Gusanos, GeneRally, Atari atd.
    http://karelik.wz.cz/
  8. Web o Lieru, Gusanos
    http://karelik.wz.cz/gusanos.php
  9. GUSANOS
    http://gusanos.sourceforge.net/
  10. GUSANOS Download
    http://sourceforge.net/projects/gusanos/
  11. Lua
    http://www.linuxexpres.cz/praxe/lua
  12. Lua
    http://cs.wikipedia.org/wiki/Lua
  13. Lua (programming language)
    http://en.wikipedia.org/wiki/Lua_(programming_language)
  14. The Lua Programming Language
    http://www.tiobe.com/index.php/paperinfo/tpci/Lua.html
  15. Lua Programming Gems
    http://www.lua.org/gems/
  16. LuaForge
    http://luaforge.net/
  17. Forge project tree
    http://luaforge.net/softwaremap/trove_list.php
  18. SdlBasic home page
    http://www.sdlbasic.altervista.org/main/
  19. SdlBasic examples
    http://nitrofurano.linuxkafe.com/sdlbasic/
  20. SdlBasic na Wikipedii
    http://en.wikipedia.org/wiki/SdlBasic
  21. Simple DirectMedia Layer
    http://en.wikipedia.org/wiki/Simple_DirectMedia_Layer
  22. SDLBASIC – The high-level interpreter for all?
    http://openbytes.wordpress.com/2008/11/08/sdlbasic-the-high-level-interpreter-for-all/
  23. FreeBasic home page
    http://www.freebasic.net/
  24. FreeBASIC (Wikipedia EN)
    https://en.wikipedia.org/wiki/FreeBASIC
  25. FreeBASIC Wiki
    http://www.freebasic.net/wiki/wikka.php?wakka=FBWiki
  26. FreeBASIC Manual
    http://www.freebasic.net/wiki/wikka.php?wakka=DocToc
  27. FreeBASIC (Wikipedia CZ)
    http://cs.wikipedia.org/wiki/FreeBASIC
  28. The Griffon Legend
    http://syn9.thingie.net/?table=griffonlegend
  29. Seriál Letní škola programovacího jazyka Logo
    http://www.root.cz/serialy/letni-skola-programovaciho-jazyka-logo/
  30. Scratch: oficiální stránka projektu
    http://scratch.mit.edu/
  31. Scratch: galerie projektů vytvořených ve Scratchi
    http://scratch.mit.edu/galleries/browse/newest
  32. Scratch: nápověda
    file:///usr/share/scratch/Help/en/index.html
  33. Scratch: obrazovky nápovědy
    file:///usr/share/scratch/Help/en/allscreens.html
  34. Scratch (Wikipedie CZ)
    http://cs.wikipedia.org/wiki/Scratch
  35. Scratch (programming language)
    http://en.wikipedia.org/wiki/Scratch_(programming_language)
  36. Scratch Modification
    http://wiki.scratch.mit.edu/wiki/Scratch_Modification
  37. Scratch Lowers Resistance to Programming
    http://www.wired.com/gadgetlab/2009/03/scratch-lowers/
  38. Snap!
    http://snap.berkeley.edu/
  39. Prostředí Snap!
    http://snap.berkeley.edu/snapsource/snap.html
  40. Alternatives to Scratch
    http://wiki.scratch.mit.edu/wiki/Alternatives_to_Scratch
  41. Basic-256 home page
    http://www.basic256.org/index_en
  42. Basic-256 Language Documentation
    http://doc.basic256.org/doku.php
  43. Basic-256 Art Gallery
    http://www.basic256.org/artgallery
  44. Basic-256 Tutorial
    http://www.basic256.org/tutorials
  45. Why BASIC?
    http://www.basic256.org/whybasic
  46. A book to teach ANYBODY how to program a computer (using BASIC)
    http://www.basicbook.org/
  47. BASIC Computer Games (published 1978) - Hammurabi
    http://atariarchives.org/basicgames/showpage.php?page=78
  48. Hamurabi - zdrojový kód v BASICu
    http://www.dunnington.u-net.com/public/basicgames/HMRABI