3D ve Flash playeru 10
Tak konečně je to tu. Flash umí 3D!! Zní to super, že? Nicméně není to až tak zásadní, jak by člověk čekal. Pod pojmem 3D si totiž většina lidí představí plně 3D renderovanou scénu, něco, na co jsou již zvyklí z počítačových her dnešních dnů. Tady přichází zklamání, opravdové 3D se nekoná.
Druhá vlastnost toho, co vnímá většina lidí jako „pravé“ 3D, je to, že vše je počítáno přes 3D chip na grafické kartě. To má nesporné výhody, zpracování dat a jejich zobrazení je mnohem rychlejší, procesor není zatížen a chip samotný je navržený tak, aby silně optimalizoval početní operace, které jsou při počítání 3D potřeba.
Ani jednou z těchto vlastností nový Flash player 10 neoplývá, nicméně nevadí to zas tak moc, jak by člověk čekal. Prvně je třeba si uvědomit cenu, kterou za to „pravé“ 3D platíme. První položkou je přenositelnost mezi platformami. Srovnejme si třeba Windows a Linux. Pod Windows máte možnost drtivou většinu her hrát pouze přes DirectX, což je sice skvělá technologie na vývoj her, ale není vůbec přenositelná pod Linux / Mac, jelikož je to technologie Microsoftu a není otevřená. Otevřený standart 3D renderování nabízí například OpenGL, přes který také běží všechny hry, co například pod Linuxem spustíte. Pokud by tedy Flash player 10 implementoval něco na způsob DirectX a spoléhal při tom na ovladače 3D karty, fungovaly by vám 3D aplikace jen na některých počítačích a celá pointa Flash playeru, jako cross-platform řešení by byla v tahu. Proto je jen dobře, že Adobe sáhla po tomto řešení. Někdo může namítat, že například MS SilverLight nabízí v očekáváné verzi 3 plnou HW podporu a to jak pro Windows tak pro MACy, ale mě, uživatele OS Linux, tím opravdu nezaujme.
Druhý trade-off je fakt, že ve Flash playeru 10 se nejedná o klasické 3D. Nově umožnuje provádět 3D transformace 2D objektů. Ale to není tak zlé, pokud si uvědomíme, že Flash umí vnořovat věci do sebe, takže při vnoření několika MovieClipů do sebe můžeme udělat víceméně 3D scénu srovnatelnou například s DOOMem a podobně. V DOOMovi na vás také neběhaly 3D postavičky, ale 2D potvory ve 3D prostředí. Proto nebudeme stahovat kalhoty před brodem a radši si to nové CS4 třidéčko vyzkoušíme v praxi, shall we?
Základ projektu
Otevřete si Flash Professional CS4, pokud ho nemáte, trial stáhnete tady https://www.adobe.com/cfusion/tdrc/index.cfm?product=flash&loc=en
Vytvoříme si nový projekt a to rovnou AIR projekt, aspoň si procvičíme i AIR. Kód zde ukázaný samozřejmě můžete používat i v normální neAIR aplikacích, pouze si ho musíte upravit pro třídu, kterou budete používat. V AIRu je vždy jedna Document Class, tzn. třída dokumentu, která je spuštěna ve chvíli, kdy se nainicializuje váše aplikace. Jelikož se pohybujeme na desktopu, tak uvažovat v intecích preloaderů nemá smysl, vše máme již na disku a spouštíme to jako jakoukoliv jinou aplikaci. Tzn. jakmile spustíme AIR aplikaci, má zcela jistě nataženu hlavní třídu a žádný preloader není potřeba.
Dále si vytvoříme naší dokumentovou třídu, ale nejprve musíme vytvořit balíček, tzn. package. Použití packages není téma tohodle tutoriálu, proto stačí použít prázdné package name.
package { // sem patri definice tridy }
Dále si naimportujeme vše, co budeme pro tento příklad potřebovat. Pomocí klauzule „import“ říkáme Flashi, že má přidat do zkompilovaného swf i tyto třídy:
import flash.display.Loader; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; import flash.net.URLRequest; import flash.geom.Point;
Jak vidíme, ve verzi 10 nám přibyly některé nové třídy, které budeme používat, takže si je postupně projdeme.
flash.display.Loader
Tuto třídu používáme pro načítání externích swf, obrázků a dalších komponent, které při práci používáme.
flash.display.Sprite
Sprite je základní stavební jednotkou každého Display Listu, tzn. něco jako MovieClip, ale s tím rozdílem, že neobsahuje vlastní časovou osu (timeline).
flash.display.StageAlign
Tato třída předává vlastnosti hodnot pro stage.align. Bez ní bychom nemohli zarovnávat na hlavní stage.
flash.display.StageScaleMode
Podobně jako předchozí třída, StageScaleMode obsahuje hodnoty konstant pro scaling hlavní stage.
flash.events.Event
Event je generickou událostí. Ve Actionscriptu 3 je velmi záhodno budovat všechny aplikace na událostech, než přímých voláních metod. Docílíte tím lepší architektury aplikace. Event je základní třída, pokud potřebujete k události přidat nějaké další informace, provede její rozšíření pomocí nové třidy, která bude třídu Event rozširovat „.. class MyEvent extends Event …“.
flash.events.MouseEvent
Klasickým případem rošíření třídy Event je například tato třída, odpovídá události, která bude vyvolána při nějaké akci s myší: posunutí, kliknutí atd.
flash.net.URLRequest
Instance této třídy se používá jako parametr pro volání metody load() například na Loaderu nebo URLStreamu a dalších.
Dále si nadefinujeme vlastní třídu dokumentu.
public class ThreeD extends Sprite
Sobour s tímto kódem se tedy musí jmenovat ThreeD.as a ve Flashi musíte mít jako document třídu nastavenou ThreeD.
Dále si nadefinujeme některé proměnné a konstantu, se kterými budeme dále pracovat.
// obsahuje cestu k obrázku, který budeme otáčet public static const FLASH_PLAYER:String = "images/flash_player.jpg"; // tímto si připravíme ukazatele na instance tříd, které teprve vytvoříme public var img:Loader = null; public var pivot:Point = null; public var holder:Sprite = null;
Nyní je třeba vytvořit konstruktor této třídy:
Zde vytvoříme EventListener, který bude čekat na událost ADDED_TO_STAGE a spustí funkci doAdded().
public function ThreeD() { addEventListener( Event.ADDED_TO_STAGE, doAdded ); }
Dále si připravíme metodu, kterou budeme „uklízet“ scénu do viditelné podoby.
public function layout():void { holder.x = stage.stageWidth / 2; holder.y = stage.stageHeight / 2; }
Nyní již můžeme vytvořit metodu doAdded(). Jejím parametrem je objekt třídy Event, který jí bude předán EventListenerem, který jsme vytvořili v konstruktoru.
public function doAdded( event:Event ):void { // nastavíme zarovnání celé scény na horní levý roh stage.align = StageAlign.TOP_LEFT; // dále říkáme, že scéna se nemá resizovat podle velikosti zobrazení Flash playeru stage.scaleMode = StageScaleMode.NO_SCALE; // na hlavní stage přidáme listener, který při změně velikosti stage zavolá doResize stage.addEventListener( Event.RESIZE, doResize ); // vytvoříme vlastní objekt pro obrázek holder = new Sprite(); // vložíme holder do display listu hlavní stage, na které právě pracujeme addChild( holder ); // vytvoříme novou instanci Loaderu pro natažení obrázku do holderu img = new Loader(); // zde přidáme listender na instanci třídy LoaderInfo, který nám poskytuje informace o počtu stažených bajtů atd. dokud se objekt stahuje, potom je ten samý objekt přístupný přes img.loaderInfo img.contentLoaderInfo.addEventListener( Event.COMPLETE, doComplete ); // stáhneme obrázek pomocí load() metody Loaderu img // v rámci parametru také vytvoříme novou instanci URLRequestu! img.load( new URLRequest( FLASH_PLAYER ) ); // zatím stahující se obrázek přidáme do display listu našeho Sprite holder holder.addChild( img ); // nakonec si uklidíme layout(); }
Na konci příprav si ještě nadefinujeme metodu, kterou listener zavolá po dokončení stahování našeho obrázku.
public function doComplete( event:Event ):void { // listenery pro pohyb myší stage.addEventListener( MouseEvent.MOUSE_DOWN, doDown ); stage.addEventListener( MouseEvent.MOUSE_UP, doUp ); // vycentrujeme obrázek na střed scény img.x = 0 - ( img.width / 2 ); img.y = 0 - ( img.height / 2 ); // uklidíme layout(); }
Jako předposlední věc vytvoříme metodu, která bude reagovat na změnu velikosti stage, jelikož aplikace beží v rámci AIR a lze zvětšovat a zmenšovat okno.
public function doResize( event:Event ):void { layout(); }
Teď se již můžeme věnovat vlastním akcím, které se mají stát při pohybu myši, celá scéna je totiž již připravena. Komentáře u jednotlivých instrukcí vysvětlují zbytek.
public function doDown( event:MouseEvent ):void { // po stisku myši vytvoříme listener pro pohyb myši stage.addEventListener( MouseEvent.MOUSE_MOVE, doMove ); // vytvoříme nový bod otáčení pivot = new Point( event.stageX, event.stageY ); } public function doUp( event:MouseEvent ):void { // zrušíme listener na pohyb myši po uvolnění tlačítka myši stage.removeEventListener( MouseEvent.MOUSE_MOVE, doMove ); // nastavíme původní hodnoty otočení holderu holder.rotationX = 0; holder.rotationY = 0; } public function doMove( event:MouseEvent ):void { // zde si vytvoříme bod ve 3d scéně, okolo kterého objekt otáčíme, použijeme pro něj hodnoty z události aktuálního pohybu myši var current:Point = new Point( event.stageX, event.stageY ); // nastavíme rotace v rámci osy X a Y jako modulo 360 rozdílu mezi bodem, kde jsme kliknuli a bodem na kterém nyní máme ukazatel myši holder.rotationY = ( current.x - pivot.x ) % 360; holder.rotationX = ( current.y - pivot.y ) % 360; }
Jak vidíme, celý trik je v nových parametrech rotationY a rotationX, které má nyní každý sprite. Jejich hodnoty mohou být od -180 do 180, tzn. pokrývají celých 360 stupňů. Dále máme ještě novou property rotationZ, která se váže k ose Z (nečekaně
. O té zase něco přístě.
Ať vás provází síla…
Zde si můžete stáhnout komplet projekt.