Na tehát akkor most kellene egy bitképet húznunk a labdánkra, meg pattanásra hangot kéne kibocsátani magunkból ( de nem a fürdőben, haha :) ). Elöljáróban öt dologról szeretnék beszélni:
– csomagkezelés. A csomagok tulajdonképpen könyvtárak, amikbe valamilyen feladatspecifikus osztályokat helyezünk el. Ha már van pár száz osztályunk, akkor a kód ugyan nem átláthatóbb, de a kódszerkezet ( osztályhierarchia ) azonban igen.
– változó elnevezés. Nagyon fontos, hogy változóinknak jól felismerhető neveket adjunk, tehát minél beszédesebb egy név, annál jobb ( ugyanakkor a túl hosszú változónevek átláthatatlanná teszik a kódot ), továbbá nagyon jó gyakorlat a nem primitív változók nevének a végére biggyeszteni a típusának a rövidítését ( pl: chatSO : a chatszöveg terítésére használt SharedObject, vagy userNameARR : a userneveket tartalmazó tömb )
– esemény és hibakezelés. Az ActionScript 3 legnagyobb előnye, hogy töb száz beépített általános eseménnyel és hibaeseménnyel rendelkezik, tehát gyakorlatilag minden felmerülő hibát megtudhatunk és lekezelhetünk futásidőben, az AS2-vel ellentétben, amikor órákat szívtunk azon, hogy egy képet vajon miért nem tölt be egy adott URL-ről: mert nincs olyan kép, kódhiba van, vagy csak a security sandbox mondta be az unalmast. Többé nem kell ilyennel szenvednünk.
– skinkezelés. A skineket, hangokat, és egyéb futásidőben berántott multimédiás elemek berántását mindig egy központi osztálynak kell végeznie, ami felügyel a betöltésükre, és aztán kiszórja a többi osztálynak. És innen már csak egy lépés a Model-View-Controller programozási paradigma : a vezérlő logikát úgy kell megírnunk, hogy absztrakt módon, bármiféle komponens és skin nélkül elfusson ( MODEL ), ha az megvan, akkor megírhatjuk a CONTROLLER osztályunkat, ami összeköti a logikát a skinnel, és vezérli a skint, illetve átveszi a felhasználói eseményeket tőle. Az MVC modell GUI-t tartalmazó alkalmazásoknál kötelező, bármilyen egyéb alkalmazásnál erősen ajánlott.
– oszály szerinti logolás. Minden osztályban hozzunk létre egy addLog függvényt, amely a szülőosztály addLog függvényének passzolja a logolni kívánt szöveget, és felejtsük el a trace-t. Az osztályonkénti addLog használata azért jó, mert osztályonként kikapcsolható a logolás, ami kiterjedt osztályszerkezetnél nagy előny, továbbá hierarchikus a logolás, tehát ha kikapcsoljuk egy magasabb szinten álló osztály logolását, az alatta álló osztályok is automatikusan kikapcsolódnak, és a program egy másik részét tudjuk elemezgetni.
Mindenesetre logoljunk orrba-szájba, ártani nem árthat, viszont iszonyú sokat segít.
Akkor fogjunk is bele. A fenti elvek alapján kicsit átírjuk a labdás alkalmazásunkat. Hozzunk létre egy új projectet FootballGame néven. Ha megvan, akkor File – New – Actionscript Class. Package – nek írjuk be, hogy „logic”, Class névnek, hogy „InteractivePointLogic”, és enter. A Navigatorban láthatjuk, hogy létrehozott egy új könyvtárat ( csomagot ) logic néven, és ebbe rakta be az InteractivePointLogic osztályt. Ez az osztály tulajdonképpen a BallClip osztály az előző példából, de csak a logikát tartalmazza.
Lássuk:
package logic { // // // import flash.display.Sprite; import flash.media.Sound; import flash.events.*; // // // public class InteractivePointLogic { // // // private var father:Object; private var gravity:Number = 0.5; private var skinSP:Sprite; private var ballSND:Sound; // public var xpos:Number; public var ypos:Number; public var xspeed:Number; public var yspeed:Number;
definiáljuk az apát, a gravitációt, szükségünk van egy, a skin spriteunkat tartalmazó ( -ra mutató ), és a hangunkat tartalmazó változóra, továbbá a szokásos pozíciókra és sebességekre ( x-ből azért lett xpos, hogy véletlenül se keverhessük össze a DisplayObject-ből származtatott objektumok x, y koordinátáival ).
// // // public function InteractivePointLogic ( myRoot:MovieClip ) { // father = myRoot; addLog( "new InteractivePointLogic" ); // } // // // public function setSkin ( mySP:Sprite ):void { // skinSP = mySP; skinSP.addEventListener( MouseEvent.MOUSE_DOWN , mouseDownHandler ); skinSP.addEventListener( MouseEvent.MOUSE_UP , mouseUpHandler ); // addLog( "InteractivePointLogic.setSkin " + mySP ); // }
A konstruktor értelemszerű, a setSkinben kapjuk meg a skin(ünkre mutató) változót, aholis magunkévá tesszük, és hozzárendeljük az egéreseményeket.
// // // public function updateSkin ( ):void { // skinSP.x = xpos; skinSP.y = ypos; skinSP.rotation += xspeed; // //addLog( "InteractivePointLogic.updateSkin" ); // } // // // public function setSound ( mySND:Sound ):void { // ballSND = mySND; // addLog( "InteractivePointLogic.setSound " + mySND ); // }
Az updateSkin függvény igazítja a skin-t a logikai pozícióhoz, továbbá forgatjuk is a skint, hogy életszerűbb legyen a mozgás. A setSoundban kapjuk meg a hangunkat.
// // // public function step ( timeEvent:TimerEvent ):void { // yspeed += gravity; // if ( xpos + xspeed > 390 ) { // ballSND.play( ); xspeed *= -1; // } // if ( xpos + xspeed < 10 ) { // ballSND.play( ); xspeed *= -1; // } // if ( ypos + yspeed > 190 ) { // ballSND.play( ); yspeed -= gravity; yspeed *= -1; // } // if ( ypos + yspeed < 10 ) { // ballSND.play( ); yspeed *= -1; // } // xpos += xspeed; ypos += yspeed; // updateSkin( ); // //addLog( "InteractivePointLogic.step" ); // }
A step függvény jócskán kibővült, minden egyes keretérintéskor lejátszuk a koppanás hangot. Aztán már csak a szokásos két függvény, és az addLog van hátra:
// // // public function mouseDownHandler ( eventOBJ:MouseEvent ):void { // father.dragBall( ); // } // // // public function mouseUpHandler ( eventOBJ:MouseEvent ):void { // father.releaseBall( ); // } // // // private function addLog ( logText:String ):void { // father.addLog( logText ); // } // // // } // // // } // // //end
Most jön a vezérlőlogika. Először is szereznünk kell egy focilabda image-et, meg egy pattanás hangot, és a google segítségével ezeket találtam 2 perc alatt:
pop
A képet a Loader Display Class segítségével rántjuk be, a hangot meg a kalsszikus módon a Sound osztállyal. Annyi változás azért történt, hogy ők csak URLRequest objektumot fogadnak paraméterként, már ami a forrást illeti. Megismerkedünk még a trace paranccsal is, ami a Felx 2 Output ablakába hányja ki a paraméternek adott szöveget.
//begin // // package { // // // import flash.display.Loader; import flash.display.StageScaleMode; import flash.display.StageAlign; import flash.display.Sprite; import flash.media.Sound; import flash.net.URLRequest; import flash.events.* import flash.utils.*; // import logic.InteractivePointLogic; // // // public class FootballGame extends MovieClip { // // private var moveTimer:Timer; // private var hasSkin:Boolean; private var hasSound:Boolean; private var ballSkinLO:Loader; private var ballSkinSP:Sprite; private var ballPopSND:Sound; private var ballPoint:InteractivePointLogic; private var myBackSP:Sprite; // private var xspeed:Number; private var yspeed:Number; private var oldx:Number; private var oldy:Number; // // // public function FootballGame ( ) { // stage.frameRate = 25; stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP; // myBackSP = new Sprite( ); myBackSP.graphics.beginFill( 0x900000 , 1 ); myBackSP.graphics.lineStyle( 2 , 0xffffff , 1 ); myBackSP.graphics.moveTo( 0 , 0 ); myBackSP.graphics.lineTo( 400 , 0 ); myBackSP.graphics.lineTo( 400 , 200 ); myBackSP.graphics.lineTo( 0 , 200 ); myBackSP.graphics.lineTo( 0 , 0 ); addChild( myBackSP ); // loadSkin( ); loadSound( ); // addLog( "new FootballGame" ); // }
Az elején egy csomó mindent importálunk, ami érdekes lehet, az az, hogy hogyan hivatkozunk az általunk előbb írt, logic csomagban található InteractivePointLogic osztályra. A másik érdekesség, hogy a konstruktorban nem hozok létre külön Stage példányt, ugyanis a doksi olvasgatása közben kiderült, hogy minden DisplayObjectből származtatott osztálynak van egy stage statikus változója, ami az applet Stage-ére mutat, szal közvetlenül elérhető. A konstruktorból még megindítjuk a skin és a hang betöltését, és ellenőriznünk kell, hogy megtörténtek-e rendben ezek a dolgok, mert csak akkor indítható a mozi.
A következő blokk végzi a skin és a hang betöltését, annak megtörténtét, és az esetleges hibakezelést ( jelen esetben kiírja, ha IO hiba történt ).
// // // public function loadSkin ( ):void { // ballSkinSP = new Sprite( ); addChild( ballSkinSP ); // var urlRequest:URLRequest = new URLRequest( "soccerball.gif" ); ballSkinLO = new Loader( ); ballSkinLO.contentLoaderInfo.addEventListener( Event.COMPLETE , skinLoaded ); ballSkinLO.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR , ioErrorHandler ); ballSkinLO.load( urlRequest ); ballSkinSP.addChild( ballSkinLO ); // addLog( "FootballGame.loadSkin" ); // } // // // public function loadSound ( ):void { // var urlRequest:URLRequest = new URLRequest( "pop.mp3" ); ballPopSND = new Sound( ); ballPopSND.addEventListener( Event.COMPLETE , soundLoaded ); ballPopSND.addEventListener( IOErrorEvent.IO_ERROR , ioErrorHandler ); ballPopSND.load( urlRequest ); // addLog( "FootballGame.loadSound" ); // } // // // public function ioErrorHandler ( eventOBJ:Event ):void { // addLog( "ioError: " + eventOBJ ); // } // // // public function skinLoaded ( eventOBJ:Event ):void { // ballSkinLO.x = -20; ballSkinLO.y = -20; ballSkinLO.width = 40; ballSkinLO.height = 40; hasSkin = true; initLogic( ); // addLog( "FootballGame.skinLoaded" ); // } // // // public function soundLoaded ( eventOBJ:Event ):void { // hasSound = true; initLogic( ); // addLog( "FootballGame.soundLoaded" ); // }
Ami itt érdekes: a Loader is tulajdonképpen egy Sprite, csak egy speciális sprite, ami magára tud rántani dolgokat, de ugyanazokkal az attribútumokkal rendelkezik. A másik dolog, hogy ennek ellenére egy külön Sprite-ban helyeztem el, erre azért volt szükség, hogy a labda bitképe a központja körül tudjon forogni, mert ha csak simán a Loadert forgatnám, a forgástengely a bal fölső sarok lenne.
Ezekután már csak a maradékot kell behánynunk a vezérlőosztályba:
// // // public function initLogic ( ):void { // if ( hasSkin && hasSound ) { // ballPoint = new InteractivePointLogic( this ); ballPoint.xpos = 20; ballPoint.ypos = 20; ballPoint.xspeed = Math.random( )*5; ballPoint.yspeed = Math.random( )*5; ballPoint.setSkin( ballSkinSP ); ballPoint.setSound( ballPopSND ); // moveTimer = new Timer( 25 ); moveTimer.addEventListener( TimerEvent.TIMER , ballPoint.step ); moveTimer.start( ); // } // addLog( "FootbalGame.initLogic " + hasSkin + " " + hasSound ); // } // // // public function dragBall ( ):void { // addEventListener( MouseEvent.MOUSE_MOVE , moveBall ); moveTimer.stop( ); // addLog( "FootbalGame.dragBall" ); // } // // // public function releaseBall ( ):void { // removeEventListener( MouseEvent.MOUSE_MOVE , moveBall ); ballPoint.xspeed = xspeed; ballPoint.yspeed = yspeed; moveTimer.start( ); // addLog( "FootbalGame.releaseBall" ); // } // // // public function moveBall ( eventOBJ:MouseEvent ):void { // ballPoint.xpos = mouseX; ballPoint.ypos = mouseY; ballPoint.updateSkin( ); // xspeed = mouseX - oldx; yspeed = mouseY - oldy; // oldx = mouseX; oldy = mouseY; // addLog( "FootbalGame.moveBall" + mouseX + " " + mouseY ); // } // // // public function addLog( logText:String ):void { // trace( logText ); // } // // // } // // // } // // //end
Na, mostmár kinéz valahogy az egész. Ha esetleg nem menne, a teljes project letölthető innen.
Ha van 9-es playered, megnézheted itt élőben a cuccot.
Most aztán jöhet a kamerás irányítás.
MilGra
A cikkhez kapcsolódó hozzászólásokat a fórum "Actionscript 3" topicjában várjuk.