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.






