Sonntag, 8. Mai 2011

Actionscript 3: Eigener MovieClip

Wer meinen Blog schon seit ein paar Einträgen liest, der weiß, dass ich Actionscript hasse aber trotzdem will ich es nochmal versuchen. Diesmal gehe ich jedoch noch methodischer vor und dass will ich mit euch teilen, desshalb wird in der nächsten Zeit für Actionscript 3 von mir ein paar Pattern, Konstrukte und Klassen kommen.


Unser heutiges Problem ist, dass wenn wir nicht mit Adobe Flash CS 5 oder ähnliche Adobe Flash IDE's arbeiten, werden wir feststellen, dass das anfertigen von Animationen als Movieclips irgendwo zwischen nervig und unmöglich liegt, ohne ständig SWF's mit der Loaderklasse zu laden und zu speichern. Desshalb werden wir heute etwas konstruieren, was zweckmäßig das gleiche wie ein MovieClip macht und selbst aber rein technisch gesehen keiner ist. Beginnen wir mit der Theorie:

Wenn wir davon ausgehen, dass wir die MovieClip Klasse immer nur dann brauchen, wenn wir Animationen abspielen wollen, dann können wir davon ausgehen, dass die MovieClip Klasse sowas ähnliches ist wie ein Array von Einzelbilder, die mit einer vorher festgelegten Verzögerung nacheinander angezeigt werden. Diese Bilder müssen wir nur noch auf unserer Leinwand darstellen. Wir brauchen also ersteinmal unsere Leinwand, hierfür kann man die Stage nehmen, ich persönlich bevorzuge aber einen Grafikkontainer, den wir auf der Stage platzieren.

1.) Grafikkontainer

public class MyMovieClip extends Sprite
{
}


Wir könnten rein theoretisch genauso gut von MovieClip selbst ableiten, ich bevorzuge hierfür jedoch Sprite, es ist also geschmacks Sache.

Jetzt wo wir unseren Grafikkontainer haben, brauchen wir unser Array für die Frames und um zu wissen, welchen Frame wir gerade darstellen einen "Zeiger", der unsere Position im Array kennt.

2.) Frames + Zeiger

public class MyMovieClip extends Sprite
{
private var _frames:Array;
private var _curframe:int;

public function MyMovieClip()
{
_frames = new Array();
_curframe = 0;
}
}



_frames stellt unser Frame Array da wo wir unsere Einzelbilder für die Animation speichern.
_curframe ist unser Frame Zeiger und beinhaltet unsere derzeitige Position im Array.

Ich glaube jeder sieht hier ein Problem, das Array ist leer aber der Zeiger hat schon eine Position im Array gespeichert und würde beim abrufen Fehler verursachen, desshalb ergänzen wir unsere Klasse um eine makeFrames Funktion, die unsere einzel Frames erstellen soll.

3.) makeFrames():void


private function makeFrames():void
{
var i:int;
var grenze:int = 5; //hier kommt eure max anzahl an Frames rein

for(i = 0; i < grenze; i++) { //hier kommt euer Algorithmus mit dem die Frames erzeugt werden rein } }


Ein einfaches Grundgerüst und wenn man hier geschickt arbeitet, dann sind alle Frames für die Animation in minimaler Zeit erstellt. Der einzige Nachteil ist, dass man wirklich jeden Frame per Hand machen muss, da standardmäßig kein Interpolieren oder ähnliches vorgesehen ist. Diese ganze Klasse entstand im Zuge der Entwicklung eines Spiels, wo die Frames der Spielfigur alle einzeln entstanden sind. Wenn ihr hier euren eigenen Algorithmus habt, hält euch nichts davon ab den zu implementieren.

So, die Frames sind alle da aber die Leinwand ist noch leer, das wollen wir jetzt ändern. Hierfür verwenden wir eine Funktion drawFrame(). Diese Funktion wird uns noch nützlich werden, wenn wir die Animation erst in Gang setzen.

4.) drawFrame():void

private function drawFrame():void
{
//letzter Frame (derzeit aktueller _curframe) von der Leinwand entfernen
this.removeChild(_frames[_curframe]);

//zum neuen Frame gehen und anzeigen
_curframe++;
if(_curframe >= _frames.length)
_curframe = 0;

this.addChild(_frames[_curframe]);

}

//im Konstruktor nach Aufruf von makeFrames();
drawFrame();


Der letzte Frame (noch derzeitig aktueller Frame) wird entfernt, dann wird der neue Frame berechnet und dargestellt. Man könnte jetzt Performance argumentieren, dass es doch schneller wäre einfach alle Frames zu zeichnen und nur den derzeit aktuellen sichtbar zu machen mit visible auf true und bei den anderen auf false. Ich bin aber für eine saubere Leinwand, damit existiert wirklich nur derzeit der Frame, der auch angezeigt wird und man muss nicht erst alle durchtesten ob auch wirklich alle anderen unsichtbar sind.

Jetzt wo alles gesetzt ist fehlt nur noch der Timer um die Animation zum laufen zu bringen.

5.) Der Timer

private var _animationtmr:Timer;

//im Konstruktor
_animationtmr = new Timer(1000/60, 0); //60 FPS, unendlich viele durchläufe


und EventHandler

//Handler Funktion
private function tick(evnt:TimerEvent):void
{
//einfach nur drawFrame aufrufen und der Rest erledigt sich von allein
drawFrame();
}

//im Konstruktor nach dem anlegen des Timers
//aufruf der Funktion mit jedem Tick, Funktionsname, useCapture, priority, weakBinding
_animationtmr.addEventListener(TimerEvent.TIMER, tick, false, 0, true);


und Startfunktion, die vom Benutzer aufgerufen wird.

public function play():void
{
_animationtmr.start();
}



Dies sind die Grundlagen der Klasse, die ich mir ausgedacht habe. Ihr könnt sie natürlich so machen wie ihr es wollt, ich will einfach nur einigen den Google Aufwand sparen, den ich hatte bevor ich mir das hier ausgedacht hatte. Viel Spaß damit, wie immer ist das Benutzen auf eigene Gefahr (wobei ich hier nicht wirklich das Potenzial sehe, dass euer Rechner abfackelt :P ) und ich garantiere keine 100% Funktionalität (jedenfalls funktioniert es bei mir fehlerfrei). Erweitern könnt ihr die Klasse natürlich wie ihr es wollt.

PS: Hier noch ein kleines Update, sobald ihr mit makeFrames eure Frames erstellt habt, müsst ihr noch mit this.addChild(_frames[0]) euren Startframe auf der Leinwand anzeigen, ansonsten kann es zu unvorhergesehenden Fehlern kommen, bis hin zum Absturz der Application.

1 Kommentar:

  1. auch wenn ich das jetzt nicht nachbaue: Toll dass du Deine Versuche öffentlich machst. Solche Erklärungen helfen immer weiter. Wär doch was für`s Wiki (www.studium-informatik.net)

    AntwortenLöschen