From the monthly archives:

December 2008



Ciao Italia

by Michal Gron on December 7, 2008


3D ve Flash playeru 10

by Martin Boháč on December 7, 2008

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.

Revoluce v práci s textem

by Pavel Šimek on December 6, 2008

Když přišel Flash Player 10, všichni se rozplývali nad 3D efekty a stranou zájmu zůstávala novinka, kterou osobně považuji za zajímavější a důležitější - nový text engine. Sliboval vyřešit problémy, které nás pálí už dlouho - např. omezení při práci se systémovými fonty (nemožnost antialiasovat je, transformovat, zprůhledňovat) a k tomu přidal typografické frajeřinky, které snad ani nikdo nečekal (jako je podpora ligatur). Při letmém pohledu do dokumentace k balíčku flash.text.engine jsem tedy musel začít slintat - jedná se o nízkoúrovňové API, které snad konečně řeší vše a od základů. Zároveň mi však bylo jasné, že teď musí přijít někdo s nějakou knihovnou postavenou na tomto API, aby se nové možnosti práce s textem mohly začít opravdu využívat v každodenní praxi. No a už je to tady, Adobe přichází s Text Layout Frameworkem, který bude součástí příští verze Flexu (Gumbo). Doufám, že si v brzké době najdu čas na první experimenty s ním.

Logování ve Flexu II.

by Petr Cihelka on December 6, 2008

Minule (Logovani ve flexu) byla popsána tvorba a princip logovacího API Flexu. Dnes navážu pokračováním v tomto tématu, konkrétně půjde o vytvoření vlastního Log Targetu. Vlastní Log Target umožňuje přizpůsobení výstupu z Logerru k obrazu svému, což je dnešním cílem.

Než bude ukázána praktická část vytvoření a práce s vlastním Log Targetem, bylo by dobré popsat princip spoluprace mezi Loggerem a Log Targetem.

Zprávy, které odesíláme pomocí metod poskytovaných Loggerem (debug(), info(), error(), …), jsou “vysílány” jako Event událost, konkrétně LogEvent. Aby Log Target tyto zprávy mohl přijímat, musí nastavit na daném Loggeru listener na událost LogEvent.LOG. Nastavení listeneru na tuto událost probíhá automaticky a odchycená událost je předána do metody logEvent( event:LogEvent ), která přijatou událost zpracuje, zformátuje a odešle na výstup.

Máme tento kód:

var logTarget:ILoggingTarget = new TraceTarget();
logTarget.includeLevel = true;
logTarget.includeDate = true;
// .....
logTarget.filters = ["my.class.foo.*"];
 
// samotna registrace Log Targetu do spravce loggeru
Log.addTarget( logTarget );

Při zavolání Log.addTarget() dojde k registraci zadaného Log Targetu. Metoda addTarget() zjistí, jaké kategorie zpráv bude registrovaný Log Target přijímat. Správce Loggerů projde filtry (logTarget.filters = ["mu.class.foo.*"] ) právě registrovaného Log Targetu a porovnává je s registrem kategorií Loggerů. Pokud najde Logger, který poskytuje zprávy jež Log Target chce, zavolá Log Target a předá mu informaci o nalezeném Loggeru. Log Target na předaném loggeru nastaví výše zmíněný listener na událost LogEvent.LOG. Od této chvíle Log Target přijímá zprávy odesílané daným Loggerem.

Vlastní Log Target

Flex poskytuje tyto vestavěné Log Targety:

  • TraceTarget - vhodný pro základní logování, jako výstup je použita trace() konzole.
  • MiniDebugTarget - umožnuje přesměrovat výstup do jiného SWF souboru, pomocí LocalConnection.
  • LineFormattedTarget - základní Log Target, který nikam neposílá výstup a je často používán jako “taťka” pro vlastní loggery.

LineFormattedTarget je základním stavebním kamenem při tvorbě vlastního Log Targetu. Poskytuje základní metody, potřebné pro logování událostí. Sám o sobě vychází z třídy AbstractTarget, která definuje základní metody pro správu a příjem událostí z Loggerů. Rozdíl mezi nimi je ten, že LineFormattedTarget definuje metodu, pro základní formtováný výstup, čímž nám práci ulehčí. Kdežto AbstractTarget žádný výstup nedefinuje a je nutné si výstup formátovat osobně.

Na samotném začátku, při vytváření vlastního Log Targetu, stojíme před rozhodnutím z jaké třídy vycházet. Chceme-li pouze využít možnosti ukládání zpráv do souboru, popř. odesílání pomocí LCDS tak doporučuji vycházet z třídy LineFormattedTarget, protože pak stačí implementovat metodu internalLog(), která provádí samotný formátovaný výstup a přesměrovat na libovolný výstup.

Pokud však máme nároky vyšší, např. chci mít vlastní vlastní výstupní formát (např. datum, čas, aj.) tak již doporučuji sáhnout po metodě AbstractTarget. Bylo by možné použít i LineFormattedTarget, ale bylo by nutné implementovat nejméně dvě metody, jednu navíc z mx_internal namespace. LineFormattedTarget přijme událost do metody logEvent, zformátuje a zavolá metodu internalLog(), která provede samotné odeslání na výstup.

Ukázka použití LineFormattedTarget

Zadání problému:

Mějme AIR aplikaci a zprávy o jejím běhu chceme zaznamenávat do zadaného souboru.

Řešení:

Pro tento případ se hodí použití třídy LineFormattedTarget, protože nemáme žádné požadavky na formát zprávy a je tedy možné použít výchozí formátování.

Naše třída bude vypadat např. takto:

package
{
    import flash.filesystem.File;
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
 
    import mx.core.mx_internal;
    import mx.logging.targets.LineFormattedTarget;
 
    use namespace mx_internal;
 
    public class CustomLogger extends LineFormattedTarget
    {
       private var _file:File;
       private var _fileStream:FileStream;
 
    	public function CustomLogger()
    	{
            super();
 
            // chceme logovat dotohoto souboru, musime jej tedy otevrit pro zapis
	    _file = new File( "C:\\flexLog\\log.txt" );
            _fileStream = new FileStream();
            _fileStream.open( _file, FileMode.APPEND);
    	}
 
    	override mx_internal function internalLog(message:String):void
    	{
            // prijatou zpravu zapisujeme do souboru
            _fileStream.writeMultiByte( message + "\n", 'iso-8859-1' );
    	}
 
    }
}

Výstup v log souboru bude asi takovýto:

12/6/2008 13:58:37.293 [DEBUG] my.class.logger.Main Button click
12/6/2008 13:58:37.421 [DEBUG] my.class.logger.Main Button click
12/6/2008 13:58:47.852 [DEBUG] my.class.logger.Main Button click

Tímto jsme vytvořili vlastní Log Target, který si ponechává všechny možnosti nastavení a zároveň se nemusíme starat o formátování zprávy.

Ukázka použití AbstractTarget

Problém:

Mějme stejné zadání z předchozí ukázky, avšak tentokrát je požadavek na formát zprávy. Zprávu je nutné zapisovat ve formátu “DATE|TIME|LEVEL|CATEGORY@MESSAGE“, protože log zprávy budou následně zpracovány jiným softwarem. Zároveň je nutné, aby byl vytvořen jedinečný soubor pro každé spuštění aplikace.

Řešení:

V tomto případě bude lepší jako základ použít třídu AbstractTarget, protože máme daný přesný formát zprávy a zároveň je nutné implementovat i vlastní metodu prozápis.

Třída bude vypadat např. takto:

package
{
    import flash.filesystem.File;
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
 
    import mx.logging.AbstractTarget;
    import mx.logging.ILogger;
    import mx.logging.LogEvent;
 
    public class CustomLogger2 extends AbstractTarget
    {
       private var _file:File;
       private var _fileStream:FileStream;
 
    	public function CustomLogger2()
    	{
            super();
 
	    _file = new File( "C:\\flexLog\\log.txt" );
            _fileStream = new FileStream();
            _fileStream.open( _file, FileMode.APPEND);
    	}
 
        /**
         * metoda, ktera zpracovava prijate LogEvent udalosti
         */
    	override public function logEvent(event:LogEvent):void
    	{
    	    var date:Date = new Date();
    	    var message:String = "";
 
    	    // vlozime datum
    	    message += Number(date.getMonth() + 1).toString() + "." +
                       date.getDate().toString() + "." +
                       date.getFullYear().toString();
 
            // separator
            message += "|";
 
            // vlozime cas
            message += padTime(date.getHours()) + ":" +
                        padTime(date.getMinutes()) + ":" +
                        padTime(date.getSeconds()) + "." +
                        padTime(date.getMilliseconds(), true);
            //separator
            message += "|";
 
            // vlozime uroven
            message += LogEvent.getLevelString( event.level );
 
            // separator
            message += "|";
 
            // vlozime kategorii loggeru, ktery poslal zpravu
            message += ILogger( event.target ).category;
 
            // separator
            message += "@";
 
            // vlozime obsah prijate zpravy
            message += event.message;
 
            // zapiseme zformatovanou zpravu do souboru
            log( message );
    	}
 
        /**
         * metoda prevzata z LineFormattedTarget, upravuje zobrazeni vterin
         */
        private function padTime(num:Number, millis:Boolean = false):String
        {
            if (millis)
            {
                if (num < 10)
                    return "00" + num.toString();
                else if (num < 100)
                    return "0" + num.toString();
                else
                    return num.toString();
            }
            else
            {
                return num > 9 ? num.toString() : "0" + num.toString();
            }
        }
 
        /**
         * metoda zajisti vytvoreni jedinecneho jmena souboru
         * (pro jednoduchost neresi jakekoli konflikty, ktere by mohly
         * vzniknout, pokud by se aplikace spustila 2x v ten samy cas)
         */
        private static function _createLogName():String
        {
            var date:Date = new Date();
 
            return 'log_' + date.getTime() + '.log';
        }
 
        /**
         * metoda zapisuje zpravu do log souboru
         */
        protected function log( message:String ):void
        {
            _fileStream.writeMultiByte( message + "\n", "iso-8859-1" );
        }
    }
}

Výstup v log souboru bude vypadat asi takto:

12.6.2008|14:50:51.069|DEBUG|my.class.logger.Main@Button click
12.6.2008|14:50:51.359|DEBUG|my.class.logger.Main@Button click

Vytvořili jsme tedy Log Target, ktery zaznamenavá zprávy dle zadání.

Závěr

Možností jak logovat je spousta, snažil jsem se (snad úspěšně) popsat jak využít kvalitního logovacího API, které poskutuje Flex. Log Targety nejsou omezené pouze na zápis do souboru, lze taktéž data rozesílat pomocí LCDS Messaging protokolů, popř. pomocí DataServices zapisovat do databáze. Možností je spousta a je těžké je všechny obsáhnout, ale i tak doufám, že jsem poskytl základní návod, jak správně logovat.

MAX Fotoreport

by Martin Boháč on December 5, 2008

Přináším několik desítek cvaknutí z Milána. Cvaknutí mám přirozeně mnohem více, ale většina z nich jsou slajdy z přednášek, takže ty vám nejprve zpracuju, ať to máte s celou parádou v rámci tutoriálů.

Omluvte sníženou kvalitu fotek s mým obličejem, někdo se tak už holt narodí :)

Včera jsem pro klienta vytvářel jednoduchý screensaver a chtěl jsem si pohrát s 3D (potažmo 2.5D), které je od 10. verze Flash Playeru nativně podporováno. Nemusel jsem řešit nedostatečnou penetraci FP10, protože jej do screensaveru lze “přibalit”, ale samozřejme, než jsem vše dokončil, testoval jsem v browseru a na počítači …

Walking around Milan

by Tom Krcha on December 5, 2008


Promised MAX slides

by Tom Krcha on December 4, 2008