AS3: Fragmentation Class Tutorial Pt. 2
Hallo.
Im zweiten Teil des Turorials geht jetzt etwas mehr an Eingemachte. Da wir im ersten Teil nur eine Fläche aus grauen Kacheln erstellt haben, werden wir das ganze jetzt dahingehend verbessern, dass jede einzelne Kachel mit den entsprechenden Ausschnitt der Bilddaten einer Bilddatei gefüllt wird. Die gekachelten Sprites stellen, positioniert per for-Schleife, das Bild wieder korrekt dar. Der Unterschied zum Original ist nicht festzustellen da die Sprites nahtlos nebeneinander liegen. Aus diesen Kacheln erstellen wir als letzten Schritt ein ParticleObject um es später elegant animieren zu können.
Die folgende Skizze soll diese kompakte Beschreibung ein wenig verdeutlichen:
Der Quellcode besteht wieder aus einer *.fla Datei die im Prinzip nur zu starten der DocumentClass (’FragmentClassDC.as’) da ist. ‘FragmentLayer.as’ enthält den Code zum erstellen der Kacheln (jeweils als Partikel) aus einem übergebenen Bild und zur Steuerung der Animation der Kacheln. Die Klasse ‘ParticleObject.as’ erstellt den jeweiligen Partikel der aus einem viereckigen Sprite besteht (’BitmapDataRectangle.as’), dessen BitmapData durch einem Teilausschnitt des übergebenen Bildes ersetzt wird. Die Datei ‘QuickLoader.as’ ist nicht nur dem Namen nach nur für das laden der Bilder zuständig, sondern tut genau das.
Also dann mal los:
Das erzeugen des FragmentLayers geschiet in der DocumentClass und ist analog zum ersten Teil, geändert haben sich nur ein paar Argumente zu denen das Container DisplayObject und die geladene Bitmap vorangestellt werden:
1 2 3 4 5 6 7 8 9 | // define FragmentLayer arguments private var rectSize:int = 20; // rectangle size private var updateRate:int = 30; // update rate private var dirVector:String = "right"; // animation direction (from - to) private var dispEvent:Boolean = true; // dispatch event when animation has finished private var startNow:Boolean = false; // start immediately private var overlay:FragmentLayer; overlay = new FragmentLayer ( your_container, your_bitmap, rectSize, updateRate, dirVector, dispEvent, startNow); |
Der Constructor sieht wie folgt aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // Constructor public function FragmentLayer (container:DisplayObjectContainer, sourceBM:Bitmap, rectSize:uint, updateRate:uint = 30, dirVector:String = "right", dispEvent:Boolean = true, startNow = true) { this.container = container; this.sourceBM = sourceBM; this.rectSize = rectSize; this.updateRate = updateRate; this.dirVector = dirVector; this.dispEvent = dispEvent; this.startNow = startNow; wLen = container.width / rectSize; hLen = container.height / rectSize; partArray = new Array(); // if two times "true" then start immediately if ( initArea () && startNow ) startEffect(); } |
Für den Fall das startNow = true ist wird sofort der Effekt gestartet, da die aufgerufene Methode initArea() nach Beendigung immer ein true zurückgibt. In diesem Beispiel ziehen wir aber mal den Effektstart per späterem MausClick vor.
Die Initialisierung der rechteckigen Fläche:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // Init the rectangle area public function initArea ():Boolean { switch (dirVector) { case "left" : updateLeft(); break; case "right" : updateRight(); break; case "up" : updateUp(); break; case "down" : updateDown(); break; default : updateRight(); // call left as default break; } return true; } |
Wir haben rechts gewählt, also erfolgt das Erstellen der Kacheln durch updateRight(). Diese ordnet die Kacheln so an, dass sie später von rechts nach links zersplittern. Jede Kachel wird durch createRect() definiert.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // particle movement right to left private function updateRight ():void { for (var i:uint = 0; i < wLen; i++) { for (var j:uint = 0; j < hLen; j++) { var xPos:int = (i * rectSize); var yPos:int = (j * rectSize); createRect (xPos, yPos); } } } |
Jedes Bitmap speichert seine Bild Informationen als BitmapData. Das geladene Bild, welches wir dem Constructor übergeben haben, enthält ebenso eine BitmapData welche wir hier in die Kachel-Sprites kopieren:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // Fill the fragment pieces private function createRect (xPos:Number, yPos:Number):void { // count rectangles count++; // create rectangle with blue (default) Bitmap inside var cr:BitmapDataRectangle = new BitmapDataRectangle(rectSize); // target rectangle var rect:Rectangle = new Rectangle(0, 0, rectSize, rectSize); // target section of the source image var pt:Point = new Point(xPos, yPos); rect.x = pt.x; rect.y = pt.y; // copy BitmapData to Byte stream var bytes:ByteArray = sourceBM.bitmapData.getPixels(rect); bytes.position = 0; //destination rectangle var destRect:Rectangle = new Rectangle(0, 0, rectSize, rectSize); // write Byte stream to destination rectangle cr.getBmd.setPixels (destRect, bytes); // pass the CustomRectangle to the final create method createFragmentParticle (xPos, yPos, cr); } |
Die finale Methode zur Erzeugung erstellt nun aus den Kachel-Sprites einzelne PartikelObjects und speichert diese in einem Array. Die Methode randRange() liefert dabei eine Zufallszahl zurück welche die Bewegungen dynamischer macht. Ach ja, Fühlt euch frei und spielt ruhig mal mit den Parametern :)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | // Create a fragment particle an push it onto the array private function createFragmentParticle (xPos:int, yPos:int, rect:BitmapDataRectangle):void { // create the fragment rectangle particle = new ParticleObject( container, rect as Sprite, xPos, yPos); // -> Play with these Parameters! // define velocity particle.setVel ( randRange(-20,20), randRange(-50,50) ); // add drag particle.drag = 0.97; // set gravity particle.gravity = 0.3; // randomize particle size particle.clip.scaleX = particle.clip.scaleY = 1.0; // set shrink particle.shrink = randRange (0.5,1.03); // add fade particle.fade = 0.01; // particle clip starting alpha particle.clip.alpha = 1.0; // -> ok stop // push particle onto Array partArray.push (particle); } // Calc a random range of particle movement private function randRange (min:Number, max:Number):Number { // e.g.: 5 - (-5) = 10; 10 + (-5) var randNum:Number = (Math.random() * (max - min )) + min; return randNum; } |
Soweit ist nun die Erstellung des FragmentationLayers abgeschlossen. Der Aufruf (hier durch ein MouseClick-Event auf den Container in der DocumentClass) von startEffect() bring die Animation ins Rollen:
1 2 3 4 5 | // Start the fragmentation effect public function startEffect ():void { this.addEventListener (Event.ENTER_FRAME, frameLoop); } |
Diese wirft, soweit die Variable ‘dispEvent = true’ ist, ein Custom-Event wenn die Effekt-Animation zuende ist und stopt in jedem Fall das EnterFrame-Event:
1 2 3 4 5 6 | // Effect has finished private function effectEnd():void { this.removeEventListener (Event.ENTER_FRAME, frameLoop); if (dispEvent) dispatchEvent(new Event(EFFECT_DONE, true)); } |
Das Custom-Event fangen wir in der DocumentClass auf und wiederholen hier im Beispiel die fragmentation der beiden geladenen Bilder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | overlay.addEventListener (FragmentLayer.EFFECT_DONE, effectDoneHandler); // FragmentLayer Effect Finished private function effectDoneHandler (e:Event):void { switch (e.currentTarget.name) { case "Overlay #1" : trace ("Effect done: " + e.currentTarget.name); // ...and again... init(); break; case "Overlay #2" : trace ("Effect done: " + e.currentTarget.name); break; } } |
Das eigendliche update der ParticleObjects übernimmt der frameLoop() in der FragmentLayer.as, den die vorher erwähnte startEffect() Methode auslöst.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // Effect frame-loop private function frameLoop (event:Event):void { // increasing part of the array which has to be updated part2update += updateRate; // ...if this part is too long, correct it if (part2update > partArray.length) { part2update = partArray.length; } // loop through Array and update each one for (var i:uint = 0; i < part2update; i++) { partArray[i].update (); // remove invisible particle if (partArray[i].invisible == true) { removeParticle (i); part2update --; i--; } } } |
Der unsichtbar gewordenen Objekte entledigen wir uns per removeParticle(), welche letztendlich auch die weiter oben beschriebene effectEnd() Funktion aufruft:
1 2 3 4 5 6 7 8 9 10 11 | // Destroy a particle private function removeParticle (val:uint):void { var buffer:Array = partArray.splice (val, 1); particle = buffer.pop(); particle.destroy (); if (partArray.length < 1) { effectEnd(); } } |
Viel Spass beim Ausprobieren!















Januar 14th, 2009 at 20:44
hmm… there is a little layout error i could not get rid of. It´s the HTML declaration of special characters e.g.:
if (partArray.length < 1)
should look like this:
if (partArray.length < 1)
and so on
Admin can you please provide an opportunity to fix this? thx
Februar 27th, 2009 at 17:56
Gut!
April 6th, 2009 at 20:49
hey, coole sache. seltsam, dass hier nicht mehr kommentare zu sehen sind :) ich bin gerade dabei, das ganze mit dynamisch erzeugten shapes zu machen.. vielleicht kann ich mich da irgendwie von deiner klasse inspirieren lassen :)
viele grüße,
marvin.
Dezember 21st, 2009 at 01:48
Feines Tutorial! Wieder was zum Thema Pixelschubsen in Flash gelernt ;-)
Mercie