Ismerkedés az ActionScript 3-mal, 4. rész


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.

Kapcsolódó bejegyzések:

A cikket beküldte: MilGra (http://milgra.hu)

Szólj hozzá
a Ismerkedés az ActionScript 3-mal, 4. rész c. bejegyzéshez

- Engedélyezett HTML elemek: <a> <em> <strong> <ul> <ol> <li>
- Forráskód beküldéséhez tedd a kódot ezek közé: <pre lang="php" line="1">Kódrészlet helye itt</pre>