Jan
14

AS3: Fragmentation Class Tutorial Pt. 2

AS3 Fragmentation Effect pt.2

Hallo.

English Version

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:

AS3 Fragment Class thumbnail sketch

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!

Download Sources

4 Kommentare

  • Paul Said:

    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

  • berlin Said:

    Gut!

  • Marvin Said:

    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.

  • Snark Said:

    Feines Tutorial! Wieder was zum Thema Pixelschubsen in Flash gelernt ;-)

    Mercie

Hinterlasse ein Kommentar

Oben SEO Powered by Platinum SEO from Techblissonline