V dnešním dílu si pomocí dvou otevřených nástrojů vytvoříme jednoduchou flashovou hru - Vystřílej prales!
SWFMILL
Jedná se o konzolový nástroj, který se používá pro generování swf souborů. Do nich si pomocí swfmill mohu naimportovat obrázky, zvuky, fonty nebo filmy a použít je dále jako zdrojovou knihovnu pro další zpracování pomocí jazyka ActionScript. Nicméně lze s ním vytvořit i jednoduché statické swf bez dalších možností.
Swfmill se pouští pomocí
$ swfmill simple zdrojovysoubor.xml cilovysoubor.swf
Jako zdroj používá swfmill xml soubor s několika danými tagy. Nejzákladnější struktura je následující:
<?xml version="1.0" encoding="utf-8" ?> <movie width="320" height="240" framerate="12"> <background color="#ffffff"/> <frame/> </movie>
Základem práce v swfmill je vytvoření knihovny objektů pro další práci v ActionScriptu. Ty se umísťují do párového tagu <library>. Nejčastější tagy v sekci <library> jsou:
<clip id="obrazek" import="library/soubor.jpg" />
Pomocí <clip> vytvářím komponenty nové třídy MovieClip. Právě sem importuji externě vytvořené obrázky.
<font id="jmenoFontu" import="library/Arial.ttf" glyphs="0123456789" />
Import fontů - pomocí parametru glyphs mám možnost omezit import na několik znaků. Díky tomu nevytvářím příliš velké soubory. V knihovně mám jen to potřebné.
<sound id="zvuk" import="library/blah.mp3" />
Práce se zvukem není nejpohodlnější. Údajně aby s ním šlo dále pracovat, mělo by být mp3 mono a mít frekvenci 44,1 kHz. Musím si ho upravit pomocí příkazu lame ($ lame --resample 44.1 in.mp3 out.mp3). Já načítám zvuky přímo v ActionScriptu, proto za uvedený postup neručím a uvádím ho zde pro úplnost.
<import file="library.swf" url="library/library.swf" />
Mohu importovat také jiná swf s vlastními knihovnami, která jsem si vytvořil já nebo grafik v Adobe Flashi.
<textfield id="blokTextu" width="200" height="50" size="10" font="jmenoFontu" text="Nazdar lidi!" />
Textové pole musí mít nadefinovánu výšku a šířku.
Po vytvoření knihovny si objekty mohu rovnou v swfmill umístit, a to pomocí nepárového tagu <place />
<place id="pozadi" x="0" y="0" depth="1"/>
id identifikuje umístěný objekt s objektem v knihovně.
Statický flash z pralesa
Mám tři základní obrázky - hrocha, slona a ilustraci z džungle. Vytvořím XML, ve kterém nadefinuji jednotlivé objekty a určím jejich zdroj. Dále si objekt umístím na určité souřadnice. Pozor! Je důležité, aby byl každý objekt ve vlastní vrstvě (depth).
Obrázky, které máme k dispozici
<?xml version="1.0" encoding="utf-8" ?> <movie width="600" height="400" framerate="30"> <background color="#ffffff" /> <frame> <library> <clip id="pozadi" import="assets/Jungle.jpg" /> <clip id="slon" import="assets/Slon.png" /> <clip id="hroch" import="assets/Hroch.png" /> </library> <place id="pozadi" x="0" y="0" depth="1" /> <place id="slon" x="140" y="300" depth="2" /> <place id="hroch" x="430" y="300" depth="3" /> </frame> </movie>
Soubor uložím například jako classes.xml a zkompiluji:
$ swfmill simple classes.xml build/animace.swf
Výhody SWFMILL
-
vytváří knihovny pro další zpracování jazykem ActionScript;
-
používá standardní zápis XML, proto je práce s ním přehledná a rychlá;
-
snadná údržba a rychlá kompilace nových knihoven;
-
v novějších verzích už bez větších problémů zvládá importovat SVG grafiku. Ta se importuje ve vektorech, což je výhoda proti bitmapám, které nemůžu zvětšovat a zabírají více místa. Díky tomu lze propojit tvorbu Flashe s Inkscape.
Obvykle nám ale nestačí statický flash a chceme jej rozpohybovat. Uznejte - statický obrázek hrocha a slona z džungle nikoho nenadchne a flash používáme proto, že chceme, aby se obrázek pohyboval a případně reagoval na ovládání uživatele. To nám umožní ActionScript - jazyk, který dokáže interpretovat plugin flashe v prohlížeči. V praxi to vypadá tak, že vytvořené swf použiji jako zdroj knihoven pro programy MTASC (programuji-li v ActionScript 2) nebo haXe (ActionScript 2 a 3).
MTASC
MTASC (Motion-Twin ActionScript 2 Compiler) je otevřený kompilátor jazyka ActionScript 2. Tento jazyk dovedou v zásadě interpretovat všechny verze Adobe Flashe od verze 6, s podstatným vylepšením od verze 8. Já sám jsem ho chtěl už odpískat a programovat pouze v ActionScriptu 3, ale v poslední době ho pracovně využívám prakticky ve všech nových aplikacích. Důvod? S ActionScriptem2 si dobře poradí Flash Lite pro mobilní zařízení. Ten je totiž postaven na Adobe Flash 8.
MTASC je dobré využít jako kompilátor pro již vytvořené knihovny pomocí swfmill nebo také pro swf vytvořené v Adobe Flashi. Výhody tohoto kompilátoru proti kompilátoru od Adobe jsou publikovány na stránkách MTASC. Za sebe oceňuji rychlost kompilace, která je u MTASC objektivně vyšší.
MTASC se pouští pomocí příkazu:
$ mtasc (naše .as soubory) -swf (swf projekt)
MTASC vezme jako zdrojovou knihovnu swf soubor, který jsme specifikovali pomocí parametru -swf, kompiluje všechny uvedené .as soubory a aktualizuje swf, ve kterém nahradí všechny vložené třídy nově zkompilovanými. Pokud swf neexistuje, vytvoří se nové, můžeme tedy vytvářet flash “z ničeho”. Chceme-li zdrojové a cílové swf odlišit, přidáme parametr -out cilovysoubor.swf
K dalším parametrům patří např:
-
-header 400:100:20:a00000 - rozměry vytvořeného Flashe, snímkování, barva pozadí
-
-main - označení hlavní třídy
-
-cp - cesta ke knihovnám
Existují dva způsoby použití swfmill a MTASC.
První způsob - Skeletal Injection Method
Vytvořím xml soubor, ve kterém popíšu kostru budoucího swf s cestami k importovaným souborům.
Tuto kostru si zkompiluji například pomocí swfmill.
Vytvořím třídu ActionScript 2 se statickou metodou main ().
Konečně příkazem mtasc “vstříknu” (inject) třídy do již existujícího swf s kostrou.
$ swfmill simple kostra.xml kostra.swf $ mtasc -swf kostra.swf -keep -main skript.as -out kostraSeSkriptem.swf
Pro tuto metodu je charakteristická přítomnost statické metody main(), která musí být v hlavní třídě aplikace specifikované parametrem -main . Jedná se o vstupní bod (Entry Point), kterým se začne vykonávat kód. Přístup do swf je tedy pouze přes tuto main().
Tento způsob velmi podrobně a názorně popsal Adam Sádovský ve výborném článku Animované flash bannery pomocí open-source nástrojů. Ten článek doporučuji všem začátečníkům s MTASC. Já se o ní nebudu nyní zmiňovat, protože ji s MTASC nepoužívám a protože si ji ještě vyzkoušíme v dalším dílu věnovanému spolupráci swfmill a haXe.
Druhý způsob - Natural Entry Point Method
Postup u této metody je obrácený - nejdříve si vytvořím třídy, zkompiluji je pomocí MTASC a swfmill mi teprve potom vnutí tyto třídy jednotlivým objektům. Tato metoda byla popsána v článku The Natural Entry Point Method (Tutorial & Source Files) – Introducing A New Way to Create Flash Applications Using Swfmill and MTASC od Arala Balkana.
Výhodou tohoto způsobu je, že nepoužívá statickou metodu main(). Nevýhodou je větší počet swf souborů, se kterým musím pracovat.
Postup je následující:
- Vytvořím swf s hlavní aplikací (pomocí MTASC).
- Vytvořím swf s objekty, které si volají již vytvořené třídy.
Já tento způsob používám proto, že mi připadá názornější a více objektově orientovaný. Vytvářím si jednotlivé soubory s třídami a metodami. Ty až v posledním bodě aplikuji na určité objekty. K jednotlivým objektům tedy přistupuji zvlášť a nemusím přes hlavní soubor. Mám sice více samostatných souborů, ale výhodou takového programování je, že jednotlivé vytvořené soubory - třídy, pak používám v jiných aplikacích. Tento postup mi prostě sedne.
Střílečka Vystřílej prales
Vraťme se do pralesa. Nejdříve jsme vytvořili statický obrázek pralesa s hrochem a slonem. Nyní chci vytvořit jednoduchou střílečku, všechno rozpohybovat a trochu si zahrát. Ostatně proto Flash používám, nebo ne?
V první řadě si obohatíme scénu o další objekty. V editoru si nakreslíme potřebné objekty - startovací tlačítko a zaměřovač (já jsem v tomto případě použil Inkscape, soubory najdete přiložené k článku).
Startovací tlačítko a zaměřovač
Dále upravím XML pro swfmill. Vždy s ním začínám, protože při rozvrhu XML si udělám představu o vytvořené scéně, zároveň všem objektům připíšu nový parametr - class, kterým určím názvy tříd.
<?xml version="1.0" encoding="utf-8" ?> <movie width="600" height="400" framerate="20"> <clip import="classes.swf" /> <frame> <library> <clip id="Aplikace" class="Aplikace" /> <clip id="Pozadi" class="Pozadi" import="assets/Jungle.jpg" /> <clip id="StartButton" class="StartButton" import="assets/NewGame.png" /> <font id="badaboom" import="assets/BadaBoomCE.otf" glyphs="VYSTŘÍLEJ PRALES!" /> <clip id="Slon" class="Objekt" import="assets/Slon.png" /> <clip id="Hroch" class="Objekt" import="assets/Hroch.png" /> <clip id="Pointer" class="Pointer" import="assets/Pointer.png" /> </library> <place id="Aplikace" name="app" x="0" y="0" depth="1000" /> <place id="Pozadi" x="0" y="0" depth="1" /> </frame> </movie>
Poprvé zde importuji také fonty, s jejichž pomocí vytvořím úvodní nápis. Všimněte si, jak jsem omezil počet písem na nejnutnější znaky.
Nyní si vytvoříme jednotlivé třídy. Začneme od nejjednodušší pro zaměřovač.
Pointer.as
Nadefinujeme si pozici zaměřovače, ta bude hlídat x a y souřadnice myši ve hře.
class Pointer extends MovieClip { function Pointer(){ } function onEnterFrame () { _x = _parent._xmouse; _y = _parent._ymouse; } }
Objekt.as
Tahle třída nám bude definovat chování zvířat na obrazovce. Chování této třídy je složitější, ale v podstatě jde jen o matematický výpočet polohy objektu na obrazovce. Při inicializaci se Objekt náhodně umístí na obrazovce. Při každém novém snímku se objekt přesune úhlopříčným pohybem danou rychlostí a směrem.
Pokud se někdo chopí příležitosti, může právě v tomto bodě hru hodně vylepšit. Například lze zvyšovat rychlost pohybu při ubývání zvířat. Tím se hra stane zajímavější.
class Objekt extends MovieClip { var vX:Number = null; var vY:Number = null; function Objekt () { _x = _width + Math.random() * ( Stage.width - _width ); //Následující podmínka kontroluje, zda nám objekt nevypadl z obrazovky if ( _x > Stage.width - _width ) _x = Stage.width - _width ; _y = _height + Math.random() * ( Stage.height - _height ); //Následující podmínka kontroluje, zda nám objekt nevypadl z obrazovky if ( _y > Stage.height - _height ) _y = Stage.height - _height ; vX = 5; vY = 5; } function onEnterFrame () { _x += vX; _y += vY; if ( _x < 0 || _x > ( Stage.width - _width ) ) { vX *= -1; _x += 2 * vX; } if ( _y < 0 || _y > ( Stage.height - _height ) ) { vY *= -1; _y += 2 * vY; } } }
StartButton.as
Třída pro úvodní - startovací tlačítko. Definujeme zde jen chování při přejetí myší.
class StartButton extends MovieClip { function StartButton(){ } // Nadefinuji chování při přejetí myši function onRollOver () { this._xscale = _yscale = 95; //Posouvám objekt, protože změna velikosti probíhá z levého horního okraje. Existují i jiné způsoby, jak tuto vlastnost obelstít, tento je nejprimitivnější this._x = this._x + 5 ; } function onRollOut () { this._xscale = _yscale = 100; this._x = this._x - 5 ; } }
JungleGame.as
V hlavní třídě aplikace začínám úvodní obrazovkou. Při stisku startovacího tlačítka, smažu úvodní obrazovku a pomocí smyčky vytvořím nové instance “slonů” a “hrochů”. Dále schovám myš a zobrazím zaměřovač. Kontroluji, zda dojde ke stisknutí myši - potom si kontroluji, zda mám zaměřovač překrytý s objektem. Pokud ano - trefa, vymažu ho. Pokud počet zvířat klesne na nulu, začínám od začátku.
Pro hlavní třídu jsem si potřeboval naimportovat třídu Delegate, pomocí které volám funkce při událostech. Třída Delegate je součástí tříd v prostředí Adobe Flashe od verze 8. Protože nepředpokládám, že byste utráceli za balík od Adobe, můžete si stáhnout její volnou verzi ze stránek autora Steva Webstera Delegate.as. Je to velmi užitečná pomůcka, protože bez ní je volání funkcí v ActionScriptu 2 mnohem pracnější. Třídu si nainstalujte do složky com/dynamicflash/utils ve složce, kde máte knihovny.
import com.dynamicflash.utils.Delegate; //importuji třídu pro vytváření událostí class JungleGame extends MovieClip { var _numAnimals: Number; function JungleGame () { init (); } function init () { //ukážeme kursor myši Mouse.show(); //umístíme startovací tlačítko this.attachMovie ("StartButton", "_startButton", this.getNextHighestDepth() ); this["_startButton"]._x= 200; this["_startButton"]._y= 300; //Vytvoříme titulku this.createTextField("_title",this.getNextHighestDepth(),0,0,Stage.width,200); var tf = new TextFormat(); tf.font = "badaboom"; tf.size = 80; tf.color = "0xffcc00"; this["_title"].text = "VYSTŘÍLEJ PRALES!" ; this["_title"].embedFonts = true; this["_title"].selectable = false; this["_title"].setTextFormat ( tf ); this["_title"]._x = 50; this["_title"]._y = 100; //Volám funkci při stisknutí tlačítka var myGame = this["_startButton"].onRelease = Delegate.create(this, startGame); myGame._numElephants = 6; myGame._numHippo = 6; } private function startGame () : Void { //Hra začíná - smažu původní obrazovku this["_startButton"].removeMovieClip(); this["_title"].removeTextField(); //Načtu hodnoty pro počet objektů na obrazovce var _numElephants = arguments.caller._numElephants; var _numHippo = arguments.caller._numHippo; _numAnimals = _numElephants + _numHippo; //Vytvořím slony for ( var i = 0; i < _numElephants; i++ ) { this.attachMovie ("Slon", "_slon" + i,this.getNextHighestDepth() ); } //Vytvořím hrochy for ( var i = 0; i < _numHippo; i++ ) { this.attachMovie ("Hroch", "_hroch" + i, this.getNextHighestDepth() ); } //Schovám myš a zobrazím zaměřovač Mouse.hide(); this.attachMovie ("Pointer", "_pointer", this.getNextHighestDepth() ); } function onMouseDown () { //Při stisku myši zkontroluji, zda se mi zaměřovač nepřekrývá s nějakým objektem //Zároveň počítám for(var i in this ){ if (this[i] instanceof Objekt ) { if (this["_pointer"].hitTest(this[i])) { this[i].removeMovieClip(); _numAnimals = _numAnimals - 1; //Jestliže už nezbývá žádné zvíře if (_numAnimals == 0 ) { this["_pointer"].removeMovieClip(); init (); } } } } } }
Nyní si musím vše zkompilovat. Nejdříve třídy pomocí MTASC do jednoho swf souboru. Ten pak teprve použiji v swfmill, kde ho umístím mezi ostatní nadefinované objekty.
$ mtasc -header 1:1:20 -swf classes.swf JungleGame.as Objekt.as Pointer.as StartButton.as $ swfmill simple classes.xml build/jungle-game.swf
Upozorňuji, že prvním příkazem jsem vytvořil swf velké 1px na 1px. To vůbec nevadí, protože se jedná jen o zkompilované třídy - konečný rozměr je nadefinován v XML swfmill. Také jsem musel vyjmenovat všechny použité třídy. Tenhle krok není nutný, pokud je volám z kódu jiné uvedené třídy.
V praxi kompilace probíhá tak, že tyto dva příklady mám umístěné ve spustitelném skriptu ./make.sh. Ušetří mi to čas, protože nemusím pořád dokola psát dva příkazy.
Ještě jeden tip pro budoucí programátory
Pro spouštění funkcí při události jsme použili otevřenou třídu Delegate. Podobně užitečná je třída GDispatcher pro hlídání událostí. Ta nahrazuje komponentu EventDispatcher z balíku Adobe. Lze si ji stáhnout z gskinner.com.
Nainstaluje se do adresáře com/gskinner/events ve vaší knihovně.
MTASC doporučuji pro:
-
tvorbu swf souborů pro co nejširší okruh klientů (bannery, hry pro web i mobilní zařízení);
-
nenáročné flashové aplikace;
-
výhodou je dobrá spolupráce s swfmill;
-
kompatibilita s ActionScriptem 2. Přestože se jedná o starý jazyk, lze ho použít prakticky na všech osobních počítačích a mobilních zařízení (s výjimkou iPadu). Chcete-li mít 100% jistotu, že váš flash uvidí opravdu všichni, potom sáhněte po MTASC;
-
díky kompatibilitě s ActionScriptem 2 máte přístup k ohromnému množství různých tutoriálů a knih. Za dobu existence ActionScriptem 2 už jich je na webu a v antikvariátech pěkná řádka.
V příštím díle se budeme věnovat jazyku haXe. Ukážeme si, jak propojit swfmill s haXe a vytvoříme si MP3 přehrávač s externím playlistem.
Všechny materiály z tohoto dílu si můžete stáhnout v archivech swfmill.zip a mtasc.zip.