Knihovna LÖVE, s jejímiž základními vlastnostmi jsme se seznámili v seriálu o programovacích jazycích i knihovnách určených pro výuku počítačové grafiky, obsahuje i poměrně snadno použitelný fyzikální engine založený na známém projektu Box2D. Dnes si na dvojici demonstračních příkladů ukážeme některé možnosti, které tento fyzikální engine vývojářům přináší.

Obsah

1. Fyzikální engine implementovaný v knihovně LÖVE

2. Modul love.physics

3. Základní objekty, které jsou využívané modulem love.physics

4. Vytvoření simulovaného 2D světa

5. Vytvoření „podlahy“ v simulovaném světě

6. Přidání dvojice padajících kvádrů

7. Automatická změna stavu simulovaného světa

8. Vykreslení objektů tvořících simulovaný svět

9. Úplný zdrojový kód prvního demonstračního příkladu

10. Změna odrazivosti těles

11. Naklonění kvádru v simulovaném světě

12. Úplný zdrojový kód druhého demonstračního příkladu

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

14. Odkazy na Internetu

1. Fyzikální engine implementovaný v knihovně LÖVE

Knihovnu LÖVE, kterou jsme si popsali v předchozích částech seriálu o programovacích jazycích a knihovnách vhodných pro výuku počítačové grafiky, je možné rozdělit na dvě části. V první části nalezneme základní moduly určené skutečně pro tvorbu dvourozměrných her, interaktivních animací či grafických dem. Ve druhé, rozšiřující části, se pak jedná o moduly, které mají své uplatnění ve specifických případech. Připomeňme si ve stručnosti, že mezi základní moduly, které knihovna LÖVE nabízí vývojářům a samozřejmě taktéž studentům či dalším zájemcům o počítačovou grafiku, patří především moduly určené pro práci s plošnou (2D) grafikou (love.graphics, love.font a love.image, v menší míře pak i love.window), zvuky a hudbou (love.audio a love.sound) a se vstupními zařízeními (love.event, love.joystick, love.mouse a love.keyboard). Mezi rozšiřující moduly, které naleznou uplatnění jen v některých aplikacích, pak můžeme zařadit love.filesystem, love.math, love.timer a love.thread.

01

Obrázek 1: První snímek simulace využívající fyzikální engine z knihovny LÖVE.

2. Modul love.physics

Dnes se budeme zabývat popisem pravděpodobně nejsložitějšího modulu, který lze v knihovně LÖVE nalézt. Jedná se o modul nazvaný love.physics, který programátorům zprostředkovává přístup ke známému fyzikálnímu engine Box2D. S využitím tohoto engine je možné simulovat vzájemné interakce (kolize a odrazy) pevných dvojrozměrných těles, vytvářet složitější objekty složené ze vzájemně provázaných základních tvarů (shape) a těles (body) i ovlivňovat trajektorii pohybujících se těles s využitím externích sil, které mohou na tato tělesa různým způsobem působit. Asi nejčastěji využívanou externí silou je pochopitelně gravitace. Taktéž je možné libovolnou část tělesa navázat ke kurzoru myši, takže se současně s pohybem myši pohybuje i tato část tělesa, která může ovlivnit i části další, s nimiž je spojena s využitím programově vytvořené vazby. To jsou však již poněkud složitější případy, kterými se dnes nebudeme zabývat. Naopak se zaměříme na popis základních konceptů, na nichž je modul love.physics postaven a vytvoříme si jednoduchý dvourozměrný svět s několika padajícími a vzájemně se odrážejícími tělesy.

02

Obrázek 2: Další snímek ze simulace využívající fyzikální engine z knihovny LÖVE.

3. Základní objekty, které jsou využívané modulem love.physics

Před použitím modulu love.physics ve vlastních aplikacích je nutné pochopit, s jakými objekty (ve smyslu objektově orientovaného programování, nikoli ve smyslu reálných objektů ve vykreslované scéně) engine tohoto modulu pracuje. Většina operací, tj. především vlastní simulace pohybu těles, pracuje s pěti typy objektů, my si však prozatím vystačíme se čtyřmi typy: World, Body, Shape a Fixture:

03

Obrázek 3: Vztah mezi čtyřmi základními typy objektů: World, Body(ies), Shape(s) a Fixture(s).

Popišme si tyto typy objektů podrobněji:

  1. World – tento objekt představuje celý dvojrozměrný „svět“, ve kterém simulace pohybu a vzájemné interakce těles (kolize, odrazy) probíhá. Jedná se o obdélníkovou oblast, jenž představuje hranice, ve kterých by měly ležet vrcholy všech těles, přesněji řečeno všech tvarů, ze kterých se tělesa skládají. Nastavená hranice by měla být dostatečně velká, aby viditelná tělesa tuto hranici nemohla překročit, protože simulace probíhají pouze uvnitř simulovaného světa (sami si můžete ve starších verzích knihovny LÖVE vyzkoušet, co se stane, když bude hranice ležet v ploše obrazovky a těleso tuto hranici překročí – pohyb tělesa se zastaví a přestanou pracovat i detektory kolizí, nastavení hranice však už v novějších verzích knihovny LÖVE není nutné použít). Na druhou stranu příliš rozsáhlý simulovaný svět může zpomalovat výpočty vzájemných kolizí těles. Při vytváření simulovaného světa lze zvolit velikost tíhového zrychlení (tj. vlastně sílu gravitace a dokonce i její vektor).
  2. Body – tento objekt představuje nestlačitelnou hmotu umístěnou v simulovaném světě. Při vytváření tělesa lze nastavit jeho souřadnice v rámci simulovaného světa, úhel natočení, hustotu, těžiště (v kontextu této knihovny se jedná o bod, ve kterém je soustředěna hmotnost tělesa) i další fyzikální parametry. V případě, že je hmotnost tělesa nulová, nepůsobí na těleso gravitace. Existují i speciální typy těles nazývané střely (bullets), které se od „běžných“ těles odlišují především použitím přesnějšího (a bohužel také pomalejšího) algoritmu použitého pro detekci kolizí. V případě, že by byl použit základní algoritmus detekce kolizí a v simulovaném světě by se velkou rychlostí pohybovala například střela proti tenké stěně, mohlo by se stát, že by střela touto stěnou prolétla, aniž by byla kolize správně vyhodnocena. Použitím střel se budeme zabývat v navazujících částech tohoto seriálu.
  3. Shape – geometrický tvar, který může být přiřazený k tělesu. Tvar představuje hranici tělesa, která je použita při detekci kolizí. V současné verzi knihovny love.physics je repertoár použitelných tvarů omezen pouze na konvexní uzavřené polygony a kruhy, protože výpočty kolizí s nekonvexními tvary jsou složitější než s tvary konvexními (ve skutečnosti se nejedná přímo o omezení knihovny love.physics, ale omezení dané C++ knihovnou Box2D, jejíž funkce a metody jsou interně volány). S využitím tvaru přiřazeného k tělesu je možné vypočítat i celkovou hmotnost tělesa a souřadnice jeho těžiště.
  4. Fixture – tento objekt zjednodušeně řečeno představuje vazbu mezi již dříve vytvořeným tvarem (shape) a tělesem (body). V nejjednodušším případě musí programátor pouze vytvořit těleso, následně specifikovat jeho tvar a spojit tyto dva objekty s využitím fixture. Existují i složitější případy, kdy je například nutné zadat odrazivost tělesa atd. Tyto případy si taktéž postupně vysvětlíme.
  5. Joint – tento pátý typ objektu (který nebyl na třetím obrázku zobrazen) představuje vazbu mezi tělesy, které se nachází v simulovaném světě. Existuje několik typů vazeb, například vazba, u níž je zadána vzdálenost dvou těles, přičemž simulační engine se snaží tato tělesa natočit a posunout tak, aby byla zvolená vazba splněna (samotný objekt představující vazbu je neviditelný, ovšem samozřejmě je možné například získat souřadnice koncových bodů vazby a ty vykreslit formou úseček). Je však možné použít i další typy vazeb, například vazbu představující kladku či páku apod. V dnešních demonstračních příkladech nejsou vazby použity, zabývat se jimi budeme až v navazujících článcích.

04

Obrázek 4: Další snímek ze simulace využívající fyzikální engine z knihovny LÖVE.

4. Vytvoření simulovaného 2D světa

Výše uvedený popis typů objektů, s nimiž je nutné v modulu love.physics pracovat, je sice obsáhlý a pravděpodobně i poněkud nesrozumitelný, ve skutečnosti je však vytvoření reálné fyzikální simulace poměrně přímočaré – ostatně náš první demonstrační příklad má pouhých 128 řádků zdrojového kódu, a to obsahuje relativně velké množství komentářů. Podívejme se tedy na to, jak lze vytvořit simulovaný fyzikální svět obsahující nepohyblivou „podlahu“ a dva kvádry, které na tuto podlahu padají (označení „kvádr“ sice není ve 2D světě přesné, ovšem o tělesech je vhodné uvažovat jako o reálných předmětech s hmotností a odrazivostí, nikoli o nehmotných 2D „papírcích“). Kostra demonstračního příkladu se prakticky ničím neliší od všech předchozích demonstračních příkladů – nachází se zde jen několik prozatím prázdných callback funkcí volaných při vzniku různých typů událostí:

--
-- Knihovna LÖVE
--
-- Osmnáctý demonstrační příklad
--
-- Jednoduchý "svět", v němž se nachází dva kvádry.
--
 
 
 
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    ...
    ...
    ...
end
 
 
 
--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    ...
    ...
    ...
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    ...
    ...
    ...
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    ...
    ...
    ...
end
 
 
 
--
-- finito
--

Do zdrojového kódu přidáme volání funkcí, které vytvoří simulovaný svět. Nejprve je nutné nastavit měřítko pro přepočet mezi pixely a metry; v našem konkrétním případě odpovídá 64 pixelů jednomu metru v simulovaném světě. Proč je však nutné nastavovat měřítko? Mnohé parametry (tíhové zrychlení atd.) jsou specifikovány s využitím fyzikálních jednotek, zatímco při vykreslování či při čtení pozice kurzoru myši se používají bezrozměrné pixely. Následně se svět skutečně vytvoří zavoláním funkce love.physics.newWorld(), které se předá vektor představující tíhové zrychlení (způsobené pro zjednodušení pouze gravitační silou):

-- tíhové zrychlení
G = 9.81
 
-- počet pixelů odpovídající jednomu metru v simulovaném světě
pixels_per_meter = 64
 
 
 
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    -- nastavení rozměrů simulovaného světa
    -- (mapování fyzikálních jednotek na pixely)
    love.physics.setMeter(pixels_per_meter)
 
    -- vytvoření simulovaného světa
    world = love.physics.newWorld(0, G * pixels_per_meter, true)
 
    ...
    ...
    ...
end

05

Obrázek 5: Další snímek ze simulace využívající fyzikální engine z knihovny LÖVE.

5. Vytvoření „podlahy“ v simulovaném světě

Nové těleso se vytváří pomocí funkce love.physics.newBody(), jejímž prvním parametrem je objekt typu „svět“, dále se této funkci předá umístění tělesa (těžiště) v simulovaném světě (čtvrtý parametr je nepovinný, použijeme ho až v další kapitole). Další operací, kterou je nutné i u jednoduchých simulovaných světů provést, je volba a přiřazení tvaru (shape) ke každému tělesu. Jak jsme si již řekli v předchozích kapitolách, je možné pro tvar tělesa použít například konvexní polygon či kruh. V demonstračních příkladech se používá polygon ve tvaru obdélníku vytvářený funkcí (opět ne metodou!) nazvanou love.physics.newRectangleShape(). Parametry předané této funkci specifikují rozměry obdélníku (nikoli však umístění v simulovaném světě, to již známe). Následně se těleso musí spojit s tvarem pomocí objektu typu fixture. Poslední parametr při vytváření tohoto typu objektu je hustota tělesa (hmotnost se vypočte automaticky):

--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    ...
    ...
    ...
    -- těleso představující zemi
    objects.ground = {}
    -- počáteční umístění tělesa
    objects.ground.body = love.physics.newBody(world, width/2, height-ground_height/2)
    -- tvar a rozměry
    objects.ground.shape = love.physics.newRectangleShape(width, ground_height)
    -- propojení tvaru s tělesem
    objects.ground.fixture = love.physics.newFixture(objects.ground.body, objects.ground.shape, 5)
    ...
    ...
    ...
end

06

Obrázek 6: Další snímek ze simulace využívající fyzikální engine z knihovny LÖVE.

6. Přidání dvojice padajících kvádrů

Prakticky stejným způsobem, jako se vytvořila „podlaha“, se do simulovaného světa přidá dvojice padajících kvádrů. Ty se od podlahy odlišují jen jediným detailem: mohou se pohybovat, což se fyzikální engine dozví ze čtvrtého nepovinného parametru funkce love.physics.newBody. Pokud je v tomto parametru uveden řetězec "dynamic", bude se dané těleso pohybovat a reagovat na na něj působící síly. Takže si zopakujme celý postup:

  1. Vytvoří se „těleso“ funkcí love.physics.newBody, z něj se systém dozví jeho umístění a charakteristiky
  2. Vytvoří se „tvar“ funkcí love.physics.newRectangleShape, z něj se systém dozví fyzikální rozměry
  3. „Těleso“ a „tvar“ se spojí pomocí love.physics.newFixture, z této funkce se systém navíc dozví hustotu tělesa (hmotnost si už vypočítá sám)
--
-- Funkce volaná při inicializaci aplikace.
--
function love.load()
    ...
    ...
    ...
    -- těleso představující první kvádr
    objects.block1 = {}
    -- počáteční umístění tělesa
    objects.block1.body = love.physics.newBody(world, 200, 200, "dynamic")
    -- tvar a rozměry
    objects.block1.shape = love.physics.newRectangleShape(40, 150)
    -- propojení tvaru s tělesem
    objects.block1.fixture = love.physics.newFixture(objects.block1.body, objects.block1.shape, 5)
 
    -- těleso představující druhý kvádr
    objects.block2 = {}
    -- počáteční umístění tělesa
    objects.block2.body = love.physics.newBody(world, 225, 50, "dynamic")
    -- tvar a rozměry
    objects.block2.shape = love.physics.newRectangleShape(100, 50)
    -- propojení tvaru s tělesem
    objects.block2.fixture = love.physics.newFixture(objects.block2.body, objects.block2.shape, 5)
    ...
    ...
    ...
end

07

Obrázek 7: První snímek z vylepšené simulace využívající fyzikální engine z knihovny LÖVE.

7. Automatická změna stavu simulovaného světa

V demonstračním příkladu je simulovaný svět představován objektem uloženým do globální proměnné world. Změna stavu tohoto světa je z hlediska programátora triviální – stačí pravidelně volat metodu world:update() a předat jí počet „tiků“ čítače času. Ve skutečnosti tedy stačí v demonstrační aplikaci provést následující jednoduchou změnu v callback funkci love.update:

--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    -- přepočítat parametry celého "světa"
    world:update(dt)
 
    local delay = 1/30
    if dt < delay then 
        love.timer.sleep(delay - dt)
    end
end

08

Obrázek 8: Další snímek z vylepšené simulace využívající fyzikální engine z knihovny LÖVE.

8. Vykreslení objektů tvořících simulovaný svět

Poněkud komplikovanější je vykreslení všech objektů tvořících náš simulovaný svět. Jedná se jak o „podlahu“, tak i o oba pohybující se kvádry. Veškeré vykreslování se samozřejmě provádí v callback funkci love.draw() a programátor musí explicitně určit, jaké tvary se mají vykreslit. Můžeme zde s výhodou použít metodu getPoints() objektů typu Shape a její výstup transformovat s využitím metody getWorldPoints() objektů typu Body. Souřadnice vrcholů mnohoúhelníku se pak předají funkci love.graphics.polygon(). Způsob vykreslení je ukázán níže:

--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- vykreslení země
    love.graphics.setColor(72, 160, 14)
    love.graphics.polygon("line", objects.ground.body:getWorldPoints(objects.ground.shape:getPoints()))
 
    -- vykreslení prvního tělesa
    love.graphics.setColor(250, 150, 150)
    love.graphics.polygon("line", objects.block1.body:getWorldPoints(objects.block1.shape:getPoints()))
 
    -- vykreslení druhého tělesa
    love.graphics.setColor(150, 150, 250)
    love.graphics.polygon("line", objects.block2.body:getWorldPoints(objects.block2.shape:getPoints()))
 
    -- výpis zprávy
    love.graphics.setColor(250, 250, 250)
    love.graphics.print("Escape: to exit.", 20, 615)
end

09

Obrázek 9: Další snímek z vylepšené simulace využívající fyzikální engine z knihovny LÖVE.

9. Úplný zdrojový kód prvního demonstračního příkladu

Podívejme se nyní na úplný zdrojový kód dnešního prvního demonstračního příkladu. Jak již bylo napsáno v úvodních kapitolách, má celý kód délku pouze 128 řádků, přičemž je použito relativně velké množství komentářů, takže programových řádků je ve skutečnosti mnohem méně:

--
-- Knihovna LÖVE
--
-- Osmnáctý demonstrační příklad
--
-- Jednoduchý "svět", v němž se nachází dva kvádry.
--
 
 
 
-- rozměry okna, do něhož se bude provádět vykreslování
width = 600
height = 600
ground_height = 50
 
-- tíhové zrychlení
G = 9.81
 
-- počet pixelů odpovídající jednomu metru v simulovaném světě
pixels_per_meter = 64
 
 
 
--
-- 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)
 
    -- inicializace grafického režimu
    love.graphics.setMode(width, height, false, false, 0)
 
    -- nastavení rozměrů simulovaného světa
    -- (mapování fyzikálních jednotek na pixely)
    love.physics.setMeter(pixels_per_meter)
 
    -- vytvoření simulovaného světa
    world = love.physics.newWorld(0, G * pixels_per_meter, true)
 
    -- tabulka s tělesy
    objects = {}
 
    -- těleso představující zemi
    objects.ground = {}
    -- počáteční umístění tělesa
    objects.ground.body = love.physics.newBody(world, width/2, height-ground_height/2)
    -- tvar a rozměry
    objects.ground.shape = love.physics.newRectangleShape(width, ground_height)
    -- propojení tvaru s tělesem
    objects.ground.fixture = love.physics.newFixture(objects.ground.body, objects.ground.shape, 5)
 
    -- těleso představující první kvádr
    objects.block1 = {}
    -- počáteční umístění tělesa
    objects.block1.body = love.physics.newBody(world, 200, 200, "dynamic")
    -- tvar a rozměry
    objects.block1.shape = love.physics.newRectangleShape(40, 150)
    -- propojení tvaru s tělesem
    objects.block1.fixture = love.physics.newFixture(objects.block1.body, objects.block1.shape, 5)
 
    -- těleso představující druhý kvádr
    objects.block2 = {}
    -- počáteční umístění tělesa
    objects.block2.body = love.physics.newBody(world, 225, 50, "dynamic")
    -- tvar a rozměry
    objects.block2.shape = love.physics.newRectangleShape(100, 50)
    -- propojení tvaru s tělesem
    objects.block2.fixture = love.physics.newFixture(objects.block2.body, objects.block2.shape, 5)
end
 
 
 
--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    -- přepočítat parametry celého "světa"
    world:update(dt)
 
    local delay = 1/30
    if dt < delay then 
        love.timer.sleep(delay - dt)
    end
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- vykreslení země
    love.graphics.setColor(72, 160, 14)
    love.graphics.polygon("line", objects.ground.body:getWorldPoints(objects.ground.shape:getPoints()))
 
    -- vykreslení prvního tělesa
    love.graphics.setColor(250, 150, 150)
    love.graphics.polygon("line", objects.block1.body:getWorldPoints(objects.block1.shape:getPoints()))
 
    -- vykreslení druhého tělesa
    love.graphics.setColor(150, 150, 250)
    love.graphics.polygon("line", objects.block2.body:getWorldPoints(objects.block2.shape:getPoints()))
 
    -- výpis zprávy
    love.graphics.setColor(250, 250, 250)
    love.graphics.print("Escape: to exit.", 20, 615)
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    if k == 'escape' then 
    -- klávesou "Escape" se aplikace ukončí
        love.event.quit()
    end
end
 
 
 
--
-- finito
--

10

Obrázek 10: Další snímek z vylepšené simulace využívající fyzikální engine z knihovny LÖVE.

10. Změna odrazivosti těles

Simulace vypočítaná a zobrazená dnešním prvním demonstračním příkladem má jednu poněkud problematickou vlastnost: kvádry se při dopadu na podlahu (či na druhý kvádr) prakticky neodráží, což nepůsobí přirozeně. Tuto vlastnost můžeme v dalším příkladu velmi snadno změnit, a to zavoláním metody Fixture.setRestitution(), kde se předaným parametrem může odrazivost změnit (možná by se dalo použít i slovo „pružnost“, ve skutečnosti se však při dopadu a odrazu nemění/nedeformuje tvar tělesa). Typické hodnoty parametru odrazivosti leží v rozsahu 0,0 až 1,0, kde 0,0 představuje nulovou odrazivost a 1,0 maximální odrazivost (můžete si sami vyzkoušet, co se stane v případě, že je tento parametr vyšší, než hodnota 1,0). Podívejme se na příklad, v němž se změní odrazivost podlahy:

    -- těleso představující zemi
    objects.ground = {}
    -- počáteční umístění tělesa
    objects.ground.body = love.physics.newBody(world, width/2, height-ground_height/2)
    -- tvar a rozměry
    objects.ground.shape = love.physics.newRectangleShape(width, ground_height)
    -- propojení tvaru s tělesem
    objects.ground.fixture = love.physics.newFixture(objects.ground.body, objects.ground.shape, 5)
    objects.ground.fixture:setRestitution(0.7)

11

Obrázek 11: Další snímek z vylepšené simulace využívající fyzikální engine z knihovny LÖVE.

11. Naklonění kvádru v simulovaném světě

Další změnou, kterou v simulovaném herním světě provedeme, bude přidání třetího pohybujícího se kvádru. Tento kvádr však bude nakloněný o úhel odpovídající přibližně 45°. Vzhledem k tomu, že se v knihovně LÖVE všechny úhly specifikují v radiánech, odpovídá 45° přibližně hodnotě π/4 rad. Právě tuto hodnotu použijeme při vytváření tvaru (shape) s využitím funkce love.physics.newRectangleShape(). Tuto funkci jsme doposud volali s dvěma parametry představujícími délku stran kvádru, nyní však přidáme další tři parametry: relativní pozici středu [0,0] a právě úhel natočení 45° ≈ π/4 ≈ 3.1415/4:

    -- těleso představující třetí kvádr
    objects.block3 = {}
    -- počáteční umístění tělesa
    objects.block3.body = love.physics.newBody(world, 425, 50, "dynamic")
    -- tvar a rozměry
    objects.block3.shape = love.physics.newRectangleShape(0, 0, 100, 100, 3.1415/4)
    -- propojení tvaru s tělesem
    objects.block3.fixture = love.physics.newFixture(objects.block3.body, objects.block3.shape, 5)
    objects.block3.fixture:setRestitution(0.9)

12

Obrázek 12: Další snímek z vylepšené simulace využívající fyzikální engine z knihovny LÖVE.

12. Úplný zdrojový kód druhého demonstračního příkladu

Podívejme se nyní na úplný zdrojový kód dnešního druhého a současně i posledního demonstračního příkladu. Zde se již mění odrazivost všech těles a poslední kvádr, který byl přidaný do simulovaného světa, je nakloněný:

--
-- Knihovna LÖVE
--
-- Devatenáctý demonstrační příklad
--
-- Jednoduchý "svět", v němž se nachází tři kvádry.
--
 
 
 
-- rozměry okna, do něhož se bude provádět vykreslování
width = 600
height = 600
ground_height = 50
 
-- tíhové zrychlení
G = 9.81
 
-- počet pixelů odpovídající jednomu metru v simulovaném světě
pixels_per_meter = 64
 
 
 
--
-- 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)
 
    -- inicializace grafického režimu
    love.graphics.setMode(width, height, false, false, 0)
 
    -- nastavení rozměrů simulovaného světa
    -- (mapování fyzikálních jednotek na pixely)
    love.physics.setMeter(pixels_per_meter)
 
    -- vytvoření simulovaného světa
    world = love.physics.newWorld(0, G * pixels_per_meter, true)
 
    -- tabulka s tělesy
    objects = {}
 
    -- těleso představující zemi
    objects.ground = {}
    -- počáteční umístění tělesa
    objects.ground.body = love.physics.newBody(world, width/2, height-ground_height/2)
    -- tvar a rozměry
    objects.ground.shape = love.physics.newRectangleShape(width, ground_height)
    -- propojení tvaru s tělesem
    objects.ground.fixture = love.physics.newFixture(objects.ground.body, objects.ground.shape, 5)
    objects.ground.fixture:setRestitution(0.7)
 
    -- těleso představující první kvádr
    objects.block1 = {}
    -- počáteční umístění tělesa
    objects.block1.body = love.physics.newBody(world, 200, 200, "dynamic")
    -- tvar a rozměry
    objects.block1.shape = love.physics.newRectangleShape(40, 150)
    -- propojení tvaru s tělesem
    objects.block1.fixture = love.physics.newFixture(objects.block1.body, objects.block1.shape, 5)
    objects.block1.fixture:setRestitution(0.7)
 
    -- těleso představující druhý kvádr
    objects.block2 = {}
    -- počáteční umístění tělesa
    objects.block2.body = love.physics.newBody(world, 225, 50, "dynamic")
    -- tvar a rozměry
    objects.block2.shape = love.physics.newRectangleShape(100, 50)
    -- propojení tvaru s tělesem
    objects.block2.fixture = love.physics.newFixture(objects.block2.body, objects.block2.shape, 5)
    objects.block2.fixture:setRestitution(0.7)
 
    -- těleso představující třetí kvádr
    objects.block3 = {}
    -- počáteční umístění tělesa
    objects.block3.body = love.physics.newBody(world, 425, 50, "dynamic")
    -- tvar a rozměry
    objects.block3.shape = love.physics.newRectangleShape(0, 0, 100, 100, 3.1415/4)
    -- propojení tvaru s tělesem
    objects.block3.fixture = love.physics.newFixture(objects.block3.body, objects.block3.shape, 5)
    objects.block3.fixture:setRestitution(0.9)
end
 
 
 
--
-- Funkce volaná cca 30x za sekundu
--
function love.update(dt)
    -- přepočítat parametry celého "světa"
    world:update(dt)
 
    local delay = 1/30
    if dt < delay then 
        love.timer.sleep(delay - dt)
    end
end
 
 
 
--
-- Tato funkce je volána automaticky při překreslení obsahu
-- okna či obrazovky.
--
function love.draw()
    -- vykreslení země
    love.graphics.setColor(72, 160, 14)
    love.graphics.polygon("line", objects.ground.body:getWorldPoints(objects.ground.shape:getPoints()))
 
    -- vykreslení prvního tělesa
    love.graphics.setColor(250, 150, 150)
    love.graphics.polygon("line", objects.block1.body:getWorldPoints(objects.block1.shape:getPoints()))
 
    -- vykreslení druhého tělesa
    love.graphics.setColor(150, 150, 250)
    love.graphics.polygon("line", objects.block2.body:getWorldPoints(objects.block2.shape:getPoints()))
 
    -- vykreslení třetího tělesa
    love.graphics.setColor(150, 250, 150)
    love.graphics.polygon("line", objects.block3.body:getWorldPoints(objects.block3.shape:getPoints()))
 
    -- výpis zprávy
    love.graphics.setColor(250, 250, 250)
    love.graphics.print("Escape: to exit.", 20, 615)
end
 
 
 
--
-- Callback funkce zavolaná při stisku klávesy.
--
function love.keypressed(k)
    if k == 'escape' then 
    -- klávesou "Escape" se aplikace ukončí
        love.event.quit()
    end
end
 
 
 
--
-- finito
--

13

Obrázek 13: Další snímek z vylepšené simulace využívající fyzikální engine z knihovny LÖVE: všechna zbývající tělesa jsou stabilizována.

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

Oba dva demonstrační příklady, s nimiž jsme se v dnešním článku seznámili, byly uloženy do Git repositáře umístěného na GitHubu (https://github.com/tisnik/presentations). Každý demonstrační příklad je tvořen pouze zdrojovým kódem uloženým v souboru main.lua:

# Příklad Zdrojový kód
1 example18 https://github.com/tisnik/presentations/tree/master/love/example18
2 example19 https://github.com/tisnik/presentations/tree/master/love/example19

Poznámka: demonstrační příklady jsou číslovány průběžně, protože tento článek (alespoň nepřímo) navazuje na seriál o programovacích jazycích vhodných pro výuku počítačové grafiky.

14

Obrázek 14: Snímek ze simulace, která bude popsána příště.

14. Odkazy na Internetu

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