AS3-Tutorium: Flash: Butterfly 05 external code
Dieser Artikel ist veraltet und wird künftig evtl. entfernt.
Dieser Artikel erfüllt die GlossarWiki-Qualitätsanforderungen nur teilweise:
Korrektheit: 4 (großteils überprüft) |
Umfang: 3 (einige wichtige Fakten fehlen) |
Quellenangaben: 5 (vollständig vorhanden) |
Quellenarten: 5 (ausgezeichnet) |
Konformität: 4 (sehr gut) |
AS3-Tutorium: Butterfly: Flash | Flex
Flash: Übersicht | Teil 1 | Teil 2 | Teil 3 | Teil 4 | Teil 5 | Teil 6 | Teil 7a | Teil 7b | Teil 7c | Teil 8 | Teil 9 | Teil 10
Verbesserung der vierten Version des Schmetterling-Movies
In AS3-Tutorium: Flash: Butterfly 04 variable number of rounds wurde ein Schmetterling erzeugt, der eine vom Benutzer vorgegebene Anzahl von Runden auf einem Rundkurs fliegt bevor er die Bühne verlässt.
Das Verhalten des Schmetterlings wird nicht verändert. Allerdings wird der Code von der Hauptbühne in eines externe Klasse ausgelagert.
<swf width="367" height="267">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_05_external_code/Butterfly05Flash11.swf</swf>
Musterlösung (Flash CS5)
(SVN-Repository)
Musterlösung (Flash CS4) (SVN-Repository)
Der Code des Butterfly-Movies wird in eine Klasse ausgelagert
- Die Datei
Butterfly04Flash.fla
unter dem NamenButterfly05Flash.fla
speichern und mit dieser Datei weiterarbeiten. - Klick auf
Szene 1
in der linken oberen Ecke der Bühne → Die Hauptbühne wird geöffnet. - Den Code in der Ebene
scripts
auskommentieren. (Der hier enthaltene Code wird in die DateiMain.as
ausgelagert.) - Klick auf den grauen Hintergrund außerhalb der Bühne → Alle Objekte auf der Bühne werden deselektiert.
- Im Fenster
Eigenschaften
: Im EigabefeldKlasse
den KlassenamenMain
eintragen (erster Buchstabe groß, keine Endung.as
!). Datei
→Neu
→ActionScript 3.0-Klasse
(CS5) bzw.ActionScript-Datei
(CS4 und früher) →OK
Nur in CS5: -> Klassenname:Main
→OK
Datei
→Datei speichern unter
→Main.as
(im selben Ordner wie die zugehörigefla
-Datei.)
In die Datei Main.as
wird folgender Code eingefügt:
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
public class Main extends MovieClip
{
/////////////////////////////////////////////////////////////////////////////
// MovieClips on the stage
/////////////////////////////////////////////////////////////////////////////
public var d_input: MovieClip;
public var d_butterfly_movie: MovieClip;
/////////////////////////////////////////////////////////////////////////////
// Constructor
/////////////////////////////////////////////////////////////////////////////
public function Main()
{
d_input.d_button_start.addEventListener(MouseEvent.CLICK, o_start);
}
/////////////////////////////////////////////////////////////////////////////
// Observer methods
/////////////////////////////////////////////////////////////////////////////
private function o_start(p_event: MouseEvent): void
{
d_input.visible = false;
var l_rounds_to_fly: Number = parseInt(d_input.d_input_rounds.text);
if (isNaN(l_rounds_to_fly) || l_rounds_to_fly < 0)
l_rounds_to_fly = 0;
d_butterfly_movie.roundsToFly = l_rounds_to_fly;
d_butterfly_movie.gotoAndPlay("lbStart");
}
/////////////////////////////////////////////////////////////////////////////
// End of class
/////////////////////////////////////////////////////////////////////////////
}
}
Wenn der Export des Codes aus der Ebene scripts
des Haupt-Movies
erfolgreich war, kann diese Eben komplett gelöscht werden. Sie enthält sowieso
nur noch auskommentierten Code.
Namenskonventionen
Folgende Namenskonventionen wurden im Code eingehalten:
In den Schmetterlingsmovies (Symbol/Klasse ButterflyMovie
) wurden
die im 4. Teil des Tutoriums
eingeführten „öffentlichen“ Namen
roundsToFly
(Zustandsvariable)lbStart
undlbEnd
(Label)
beibehalten, da auf diese Attribute weiterhin von außen zugegriffen wird,
und zwar vom Main-Objekt aus, d.h. von dem Objekt der Klasse Main
aus, das Flash
bei Start des Flashfilms automatisch erzeugt.
(Eigentlich wird nur auf lbStart
und nicht auf lbEnd
„von außen“ zugegriffen.)
In der Klasse Main
wurden die Moviclips
d_input
d_butterfly_movie
dagegen gemäß der Konvention für nicht öffentlich zugängliche (private
) Eigenschaften benannt,
da es keine anderen Objekte gibt (und auch nicht geben soll), die auf diese Objekte
zugreifen.
ActionScript
Packages
Eine Flash-Klasse ist immer in einem package
enhalten. Wenn die
Klasse im selben Ordner wie der Flash-Film gespeichert wird, gibt es keinen
Package-Namen. Wird dagegen die Datei beispielsweise im Unterordner
butterfly
des Unterordners tutorium
des Unterordners hsa
gespeichert, so
lautet der Package-Name hsa.tutorium.butterfly
:
package hsa.tutorium.butterfly
{
...
}
Genau eine öffentlich zugängliche Klasse
Jede Klassen-Datei <Klassenname>.as
muss genau eine öffentliche Klasse (public
)
mit dem Namen der Datei (ohne Endung .as
) enthalten. Weitere
interne Klassen können in der Datei enthalten sein, aber keine weitere
öffentlich zugängliche Klasse.
Import-Anweisungen
Wie in Java steht vor der Klasse eine Liste import
-Anweisungen,
die den direkten Zugriff auf andere Klassen im Code ermöglicht. In der
Flash-Zeitleiste müssen im Prinzip dieselben import
-Anweisungen
vorhanden sein. Allerdings werde in der Flash-Zeitleiste einige Standard-Klassen-Pakete
defaultmäßig importiert, so dass eine Angabe von import
-Anweisungen
in diesem Fall unterbleiben kann. In eine Flash-Klasse dagegen müssen stets alle notwendigen
import
-Anweisungen angegeben werden.
Namen von Objekten auf der Bühne in der Klasse deklarieren
Die Objekte, die auf der Bühne liegen und einen Objektnamen erhalten haben, sollten
in der Klasse als öffentliche (public
) Zustandsvariablen (instance variables)
deklariert werden. Sie werden von Flash automatisch initialisiert.
Wenn sie nicht als public
deklariet werden, kommt es zu Laufzeitfehlern,
da Flash die automatische Initialisierung dann nicht durchführen kann.
Man könnte auf die Deklaration dieser Variablen ganz verzichten, erhält damit aber Code, der schwerer zu lesen ist. Falls man den Code nicht mit Flash, sondern mit dem Flash Builder bearbeitet, würde man außerdem Fehlermeldungen direkt im Flash-Builder-Editor erhalten, sobald man im Code auf diese Objekte zugreift.
Überführung von Code aus der Zeitleiste in die externe Klasse
Der eigentlich Code aus der Zeitleiste des ursprünglichen Movies wird in zwei Teile geteilt:
- Im Konstruktor
public function Main()
wird derjenige Code eigefügt, der in der Zeitleiste direkt ausgeführt wird. - Hinter den Konstruktor werden alle Methoden eingefügt, die in der Zeiltleiste definiert wurden. Diese Methoden werden i. Allg. als
private
markiert, da der Zugriff auf diese Methoden i. Allg. nur innerhalb der Klasse erfolgt.
Im Code können und sollten Kommentare eingefügt werden (siehe ASDoc).
Das Problem mit der nicht-existierenden Bühne bei der Verwendung von TFL-Text-Objekten
In Flash CS5 gibt es das Problem, dass die Bühne (stage
) beim Ausfügren des Konstruktors
noch nicht initialsiert ist, wenn irgendwo auf der Bühne oder in einem Symbol ein TFL-Text
verwendet wird. Diese Texte sind sehr mächtig, aber ihre Verwendung führt leider zu Problemen.
(In Flash CS4 gibt es dieses Problem nicht, da TFL-Texte nicht unterstützt werden.
Der folgende Text ist jedoch auch für Flash-CS4-Benutzer interessant, da auch gezeigt
wird, wie man zusätzlich zur Maus-Steuerung eine Tastatur-Steuerung in einen Movie Clip integriet.)
public function Main()
{
trace(stage);
}
Das Ergebnis dieses Trace-Aufrufes sollte [object Stage]
sein, ist aber bei der Verwendung
von TFL-Text gleich null
. Dies hat zur Folge, dass Befehle,
die direkt auf die Bühne (stage
) zugreifen, nicht einfach in den Konstruktor geschrieben werden können,
da zum Zeitpunkt der Konstruktion des zugehörigen Objekte die Bühnen nicht nicht existiert.
Man erhält für derartige Befehle beim Starten des Filmes Null
-Pointer-Exceptions.
In der Zeitleiste passieren derartige Fehler nicht, da der Code im ersten Frame erst ausgeführt wird, wenn die Bühne bereits existiert.
Workaround
In Flex gibt es ein spezielles Ereignis APPLICATION_COMPLETE
, sobald die Bühne erzeugt wurde.
In Flash gibt es ein derartiges Ereignis leider nicht. Man kann sich allerdings behelfen,
indem man das Ereignis ENTER_FRAME
abfängt. Sobald der erste Frame des Movies betreten
wird, existiert die Bühne auf jeden Fall. Also tritt der Fehler nicht mehr auf.
Beispiel
<swf width="367" height="267">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_05_external_code_multi/Butterfly05MultiFlash11.swf</swf> Erweiterte Musterlösung (Flash CS5) (SVN-Repository)
Erweiterte Musterlösung (Flash CS4) (SVN-Repository)
Bislang kann der Rundflug nur per Mausklick gestartet werden. Wenn dies auch
per Tastatur möglich sein soll, muss man zusätlich einen Event Listener (Observer) für Tastatur-Ereignisse
implementieren:
stage.addEventListener(KeyboardEvent.KEY_UP, o_check_enter);
Die Methode o_check_enter
kann dabei folgendenmaßen definiert werden.
private function o_check_enter(p_event: KeyboardEvent): void
{
if (p_event.keyCode == Keyboard.ENTER) // Wenn die Enter-Taste gedrückt wird
o_start(null); // wird der Rundflug gestartet.
}
Leider ist — sofern TFL-Texte verwendet werden — das Stage-Objekt bei Ausführung des Konstruktors
noch nicht definiert. Der folgende Konstruktor erzeugt also in diesem Fall eine
null
-Pointer-Exception.
...
public function Main()
{
d_input.d_button_start.addEventListener(MouseEvent.CLICK, o_start);
// Zugriff auf stage => null-Pointer-Exception
stage.addEventListener(KeyboardEvent.KEY_UP, o_check_enter);
}
...
Das Problem lässt sich lösen, indem man die Initialisierung erst beim Betreten des ersten Frames durchführt.
...
public function Main()
{
// Wenn das Ereignis ENTER_FRAME eintritt, rufe o_init auf.
this.addEventListener(Event.ENTER_FRAME, o_init);
}
private function o_init(p_event: Event): void
{
// Da die Initialisierung nur im ersten Frame erfolgen soll,
// muss der Event-Listener gleich wieder entfernt werden.
this.removeEventListener(Event.ENTER_FRAME, o_init);
// Nun existiert das stage-Objekt!
d_input.d_button_start.addEventListener(MouseEvent.CLICK, o_start);
stage.addEventListener(KeyboardEvent.KEY_UP, o_check_enter);
}
...
Noch ein Beispiel
Für das vorherige Beispiel gibt es noch einen einfacheren Workaround. Anstelle von
stage.addEventListener(KeyboardEvent.KEY_UP, o_check_enter);
schreibt man einfach
this.addEventListener(KeyboardEvent.KEY_UP, o_check_enter);
in den Konstruktor. Dies funktioniert genauso gut. Da aber ein Zugriff auf das stage
-Objekt unterbleibt,
gibt es bei folgenden Konstruktor keine null
-Pointer-Exception:
...
public function Main()
{
d_input.d_button_start.addEventListener(MouseEvent.CLICK, o_start);
// Zugriff auf this => keine null-Pointer-Exception
this.addEventListener(KeyboardEvent.KEY_UP, o_check_enter);
}
...
Leider lassen sich Zugriffe auf das stage
-Objekt manchmal nicht so leicht vermeiden.
Wenn z.B. beim Filmstart der Eingabe-Fokus gleich auf das Eingabe-Feld gesetzt werden soll
(das Eingabe-Feld, das den Fokus hält, wird von Flash durch einen blauen Rahmen gekennzeichnet),
damit der Benutzer gleich die Rundenzahl mit der Tastatur eingeben kann und nicht erst mit
der Tab-Taste oder der Maus den Fokus auf das Eingabefeld legen muss, muss man die Methode setFocus
verwenden:
d_input.d_input_rounds.setFocus();
Die Methode setFocus
greift intern auf das stage
-Objekt zu.
Der folgende Konstruktor erzeugt also eine null
-Pointer-Exception.
(Für Flash-CS4-Nutzer ist der folgende Code allerdings in Ordnung.
Daher wurde er in der Flash-CS4-Version der erweiterten Musterlösung auch eingesetzt.)
...
public function Main()
{
// Zugriff auf this => keine null-Pointer-Exception
d_input.d_input_rounds.setFocus();
d_input.d_button_start.addEventListener(MouseEvent.CLICK, o_start);
this.addEventListener(KeyboardEvent.KEY_UP, o_check_enter);
}
...
Das TLF-Text-Problem mit dem nicht-initialiserten stage
-Objekt lässt sich hier nur lösen,
indem man wie zuvor beschrieben die Initialisierung erst beim Betreten des ersten Frames durchführt.
...
public function Main()
{
// Wenn das Ereignis ENTER_FRAME eintritt, rufe o_init auf.
this.addEventListener(Event.ENTER_FRAME, o_init);
}
private function o_init(p_event: Event): void
{
// Da die Initialisierung nur im ersten Frame erfolgen soll,
// muss der Event-Listener gleich wieder entfernt werden.
this.removeEventListener(Event.ENTER_FRAME, o_init);
// Nun existiert das stage-Objekt!
d_input.d_input_rounds.setFocus();
d_input.d_button_start.addEventListener(MouseEvent.CLICK, o_start);
this.addEventListener(KeyboardEvent.KEY_UP, o_check_enter);
//stage.addEventListener(KeyboardEvent.KEY_UP, o_check_enter); // wäre hier auch OK!
}
...
Probleme der Implementierung
Die im vierten Teil des Flash-Tutoriums
genannte Problem wurden noch nicht behoben, da nur der Code der Hauptzeitleiste ausgelagert wurde.
Die schlechte Lesbarkeit der Timeline des Symbols ButterflyMovie
sowie die Code-Duplikation,
falls ein zweites derartiges Symbol erstellt werden soll, bleiben bestehen.
Diese Probleme werden erst gelöst, wenn auch der Code des Symbols ButterflyMovie
in eine externe Klasse ausgelagert wird
(siehe AS3-Tutorium: Flash: Butterfly 06 external code).
Quellen
- Kowarschick (MMProg): Wolfgang Kowarschick; Vorlesung „Multimedia-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2018; Quellengüte: 3 (Vorlesung)
- Musterlösung (Flash CS5)
- Musterlösung (Flash CS4)
- Erweiterte Musterlösung (Flash CS4)
- Erweiterte Musterlösung (Flash CS5)