Macromedia Flash ActionScript 2.0 és ActionScript 3.0

Display programozás

1. Az AS3 display programozása

Minden alkalmazás, amelyet az AS3-ban írunk rendelkezik display objektumok egy megadott hierarchiájával, melyet display listnek hívunk.

Egy display list beli elem az alábbiak valamelyike lehet:

A display list tehát egy olyan fa, melynek gyökere a Stage, csúcsai pedig DisplayObject, de szülő csúcs csak DisplayObjectContainer lehet.

A DisplayObjectek rendkívül változatosak lehetnek, ide tartoznak például a következőek:

2. A display list előnyei

2.1. Gyorsabb, hatékonyabb renderelés

AS1-2-ben csak a MovieClip osztály volt, melyet kirajzolhattunk. Ehhez képest AS3-ban van több lényegesen egyszerűbb objektum, mely rajzolható (pl. Shape, Sprite).

Már eleve a rengeteg megspórolt memória (már hogy egy körhöz nem kell MovieClip-et és az ebből származó összes tulajdonságát és metódusát tárolni, hanem elég a Shape) is sokat javít a helyzeten, de még külön nagy javítás, hogy a MovieClip nem csak sok attribútumot tárol, de timeline-nal rendelkezik, ami a rajzolt objektumok nagyjának esetében teljesen felesleges, ennek hiányával is rengeteg memóriát és főleg számítási költséget spórolunk.

2.2. Jobb mélységkezelés

AS1-2-ben az objektumok egymásfelettiségét egy lineáris modellel ábrázolták. Ehhez képest a hierarchikus modell jóval kevesebb művelettel megállapíthatja egy objektum mélységét a Stage-en (ami pont a display list fájában való helyzetével arányos, azon belül már csak a testvérektől függ.)

AS3-ban egy DisplayObjectContainer gyermekeinek száma pontosan meghatározott, indexelt, amiben nincsenek hézagok, így azok könnyen bejárhatóak.

2.3. Teljes bejárás

AS1-2-ben a kézzel rajzolt, és nem példányosított elemek nem voltak a kódból elérhetőek. Ezzel szemben AS3-ban már a rajzolt elemek is elérhetőek a kódból.

2.4. Display listen kívüli objektumok

AS3-ban lehetséges a display listen kívüli display objektumokat létrehozni, amik így nem jelennek meg a Stage-en, így nem rendereljük őket feleslegesen, míg nincs rájuk szükség, és a háttérben nyugodtan dolgozhatunk, majd mikor készen vagyunk az addChild() ill. addChildAt() metódusokkal könnyen a display listbe helyezzük őket.

Egy DisplayObjectContaineren belüli objektumok mélységének sorrendjét az index határozza meg. Ezen kívül az index alapján a getChildAt( ) metódussal el is érhetjük az adott objektumot. Két objektum sorrendjét a swapChildren() illetve swapChildrenAt() metódusokkal tudjuk megcserélni.

3. DisplayObjectek befolyásolása

3.1. Pozicionálás

A DisplayObjecteknek van origója, a (0,0) pont a saját 2D koordinátarendszerükben. Az x és y attribútumokat használva mozgatni tudjuk a őket, ahol az (x,y) érték az objektum origójának koordinátáit jelenti a szülőobjektumának origójához viszonyítva. A Stage origója a bal felső sarok, és jobbra haladva az x, balra haladva az y koordináta nő pixelenként eggyel. Például ha a Stage-en egy 10 sugarú kört a (10,10) pontba mozgatunk akkor pont a bal felső sarokba kerül, érintve a Stage kereteit.

Egy globális helyzetet megadó Point objektumot lokálissá tehetünk az objektum globalToLocal függvényével:

	var stagePoint:Point = new Point(0, 0);
	var localPoint:Point = mySprite.globalToLocal(stagePoint);
	

A fenti körös esetben tehát a localPoint például a (-10,-10) pont lenne.

3.2. Transzformációk (transform, skew, rotate, scale, distort)

Egy objektumon végrehajthatunk a legkülönbözőbb transzformációkat, például forgathatjuk, méretezhetjük, ferdíthetjük, torzíthatjuk, színeit befolyásolhatjuk.

Objektumokat méretezni például az alábbi propertykkel lehet:

Forgatni a rotation propertyvel lehet, aminek értéke annyi fok, ahány fokkal forgatni szeretnénk, alapértelmezésben 0.

3.3. Alpha

Alphának a display objectek átlátszóságát nevezzük. Ez AS1-2-ben 0 és 100 közötti érték, ahol 0 a teljesen átlátszó, 100 az egyáltalán nem átlátszó. AS3-ban ez az érték 0 és 1 közötti lebegőpontos szám. Gyakorlatban 1-nél nagyobb szám is megadható, sőt, érdekesség, hogy ha így teszünk, akkor ha egy alobjektum részben átlátszó, átlátszósága a megadott szám szerint csökkenni fog.

Az alpha propertyvel szabályozható.

3.4. Blending

Alapértelmezésben ha két objektum fedi egymást, a felső eltakarja az alsót, azonban ennek viselkedése változtatható különböző grafikai keverésekre, megadható például invertálásnak, alpha csatornának, különbségnek és összegnek, stb.

A blendMode propertyvel lehet beállítani a BlendMode osztály konstansaira.

3.5. Maszkolás (masking)

Egy objektum lehet egy másiknak a maszkja, ekkor a maszkolt objektumnak nem, vagy csak a maszkoló objektum által metszett része látható.

Maszkolni a mask propertyvel lehet. Így maszkolhatunk például egy körrel egy képet:

myPicture.mask = myCircle;

Félig átlátszó (semi transparent) maszkot csak bitmappel tudunk csinálni (ehhez előtte egy vektorgrafikát bitmapként cache-elni kell).

3.6. Szűrők (filtering)

Objektumokra szűrőket is helyezhetünk, például elmosást, árnyékot, színkeverést, stb.

3.7. Framerate

A framerate tulajdonsággal szabályozható, hogy másodpercenként hány kockát haladjon a lejátszás.

4. Rajzolás

Néhány fontos rajzolófüggvény:

5. Display programozás Flex-szel

Mivel a Flex ramework-ben rengeteg osztály (főleg nézet-komponensek osztályai) található, ezért gondolnánk, hogy ezeknek az osztályoknak egy saját nézet-vezérlő mechanizmusa van. Egy ilyen formában igaz, viszont amire az ember nem gondolna az az, hogy az eddig leírt egyel alacsonyabb szintű rajzolási technikák ugyanúgy érvényesek rá. Erre egy példa (ami a példaprogramomban is látszik), hogy lehet akármennyire bonyolult egy nézetbeli komponensem (például egy legördülő listás menü, vagy akár egy táblázat) a scaleX és scaleY adattagjainak állításával ugyanúgy tudjuk méretezni a kívánt objektumot, pedig a méreteinek a kiszámolása és invalidálása teljesen külön történik.

Az objektumok életciklusa

Ahhoz hogy egy fejlesztő saját komponenst tudjon gyártani, ahhoz először meg kell értenie az objektumok életciklusát (Object life cycle) Flex-ben. Ennek a lényegét röviden megpróbálom összefoglalni:

Amikor egy példány létrejön, a konstruktorokban gyakorlatilag olyan változókat szokás csak beállítani, amelyek az objektum végéig úgy maradnak, tahát állapotmentesek. Ide tartozik például az a tulajdonság, hogy egy link elváltsa az egér kurzorát, amint a felhasználó ráviszi az egeret, vagy egy komponens háttérszínét itt állítjuk priosra, hogyha ez későbbiekben sem változik meg, vagy akár itt szoktunk az adott objektumra eseménykezelő függvényeket felaggatni.

Nem szabad arra hagyatkozni, hogy egy objektum akkor jön létre, amikor ki is akarjuk rajzolni. Viszont amikor ki akarjuk rajzolni, akkor valamelyik konténeren meg kell hívnunk az addChild() metódust, hogy betegyen maga alá gyereknek a Display Tree-be. Ekkor az az objektum beállítja a parent property-nket magára, illetve kiváltja az objektumonkon a "preinitialize" eseményt.

Amint a "preinitialize" esemény kiváltódott egy objektumon, ez meghívja a saját createChildren() metódusát, ugyanis ekkor van az ideje a saját gyerekeinek létrehozására. Ha örökítünk egy UIComponentből, és leszenk gyerekei az objektumunknak, akkor felül kell definiálnunk a createChildren() metódust, és itt kell létrehoznunk a saját gyerekeinket.

Amint létrehoztuk a gyerekeinket, ez néhány adattagunkat megváltoztatja. Ennek hatására meghívódnak sorban az invalidateProperties(), illetve ha tényleges változás történt akkor a commitProperties(), és ha olyan változás történt, ami alapján újra is kell rajzolnunk az objektum képét, akkor az updateDisplayList() metódus is, ezeket ki kell egészítenünk úgy, hogy az új komponensünk szemantikáját is leimplementáljuk. Ezek a függvények meghívása után kiváltódik az "initialize" esemény.

Nyilván, miután a szülő objektum kapott egy új gyereket, ezért neki az állapota, illetve az esetek többségében a kinézete is megváltozik, így a szülő komponensen is végrehajtódnak, különböző méretező, állapot-invalidáló, ha az állapot megváltozott akkor állapot helyrehozó, illetve újrarajzoló függvények.

Hogy ez miért fontos? Mutatnék is egy példát. Vegyük a lentebb található flash animációt. (Vagy akármelyik másikat a világhálón.) Jobb egérgombbal kattintsunk rá, és válasszuk ki a "Show redraw regions" menüpontot. Ez először kicsit furcsa lesz, de gyakorlatlag most a következő történik: Amint az animáció egy részét újra kell rajzolnia a player-nek, az fog az újrarajzolt rész köré egy piros négyzetet is rajzolni, tahát a piros négyzeteken kívüli részeket abszolút nem rajzolja újra a Flash player.

Hogy hogy jön ez a komponensek életciklusához? Tegyük fel, hogy van egy komponensünk, ami egy kép. Hogyha ez a kép kicsit "elforog", akkor, akkor nyilván arrébb kell rajzolnunk, illetve amit már nem takar el, ott a hátteret újra kell rajzolnunk mögötte. Tegyük fel, hogy elforgatjuk 27 fokkal. Ekkor lemegy a rotation setter függvénye, ami beállítja az új értéket 27-tel nagyobbra, ami meghívja a commitProperties függvényt, ez kiszámolja, hogy 27 fokkal arrébb forgatva az objektum egyáltalán látszik-e, és ha nem takarja le semmi, akkor meghívja az updateDisplayList() függvényét.

De vegyünk egy másik példát, most forgassuk el 0 fokkal. Ekkor a setter lefutása után ha megírtuk a logikát, ami érzékeli, hogy az érték nem változott (ez nem egy onyolult, műveletigényes dolog, elágazásokat kell elképzelni egymásban), ekkor nem is invalidálja az adattagjait, nem hív meg egy rettentő bonyolult számítást, amelyik meg akarná állapítani, hogy az adott komponens látszik-e, és ha igen újrarajzolná.

Vegyünk egy harmadik példát is, amelyben pontosan 360 fokkal forgatjuk el a képet. Ekkor lefut az adattag setter függvénye. Itt nyilván az érték változott, ez invalidálja az aktuális állapotot, ez pedig kiszámolja, hogy ez milyen hatással van a megjelenő felületre. Mivel a 360 fokos forgatás gyakorlatlilag helyben hagy mindent, ezért ez a számítás tudja detektálni, és nem fogy újrarajzolással bajlódni, ugyanis teljesen felesleges, minden ugyanúgy néz ki, int azelőtt.

Végezetül megmutatnám még egy jó tulajdonságát annak, hogy a komponensek életciklusát betartsuk. Írjuk egy ciklust, amelyik 1000-szer végrehajtja a ciklusmagot, amiben valósítsuk meg azt, hogy egy fokkal arrébbforgatjuk a képet. Ekkor ami fog történni: ciklusban elkezdjüm gehívni a setter függvényt. Ez még nem a bonyolult számítást végző commitProperties függvényt hívja, hanem egy invalidateProperties nevű függvényt, ami csak megjelöli az objektumot, hogy újra kell számolni, és csak a következő Frame előtt hívódik meg a commitProperties függvény, ami a bonyolultabb számítást végzi. Gyakorlatlilag megnöveltük 1000-el az elforgatás property-t, viszont ez egy-maximum 2 frame idő alatt lement, szóval a Flash player azt a képet maximum rajzolta újra a képernyőnkön. Nem túl kevés ez? Mivel frame-enként rajzol csak egyet a Flash player, azért a szemünk ezt még folyamatos mozgásnak veszi. Nem előbb kéne számolnunk? Nem. Azért csak a frame vége előtt hívja újra a Flash player a commitProperties függvényünket, ugyanis az érdekli, hogy a következő frame elején, amikor ténylegesen rajzol, akkor mi lesz az, amit újra kell rajzolni, hogy előtte, vagy utána esetleg nmit kéne, azzal egyenlőre nem foglalkozik.