AS3-Tutorium: Flash: Butterfly 05 external code

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg
Wechseln zu:Navigation, Suche

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

1 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.


Musterlösung (Flash CS5) (SVN-Repository)

Musterlösung (Flash CS4) (SVN-Repository)


1.1 Der Code des Butterfly-Movies wird in eine Klasse ausgelagert

  1. Die Datei Butterfly04Flash.fla unter dem Namen Butterfly05Flash.fla speichern und mit dieser Datei weiterarbeiten.
  2. Klick auf Szene 1 in der linken oberen Ecke der Bühne → Die Hauptbühne wird geöffnet.
  3. Den Code in der Ebene scripts auskommentieren. (Der hier enthaltene Code wird in die Datei Main.as ausgelagert.)
  4. Klick auf den grauen Hintergrund außerhalb der Bühne → Alle Objekte auf der Bühne werden deselektiert.
  5. Im Fenster Eigenschaften: Im Eigabefeld Klasse den Klassenamen Main eintragen (erster Buchstabe groß, keine Endung .as!).
  6. DateiNeuActionScript 3.0-Klasse (CS5) bzw. ActionScript-Datei (CS4 und früher) → OK
    Nur in CS5: -> Klassenname: MainOK
  7. DateiDatei speichern unterMain.as (im selben Ordner wie die zugehörige fla-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.

2 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 und lbEnd (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.

3 ActionScript

3.1 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
{ 
  ...
}

3.2 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.

3.3 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.

3.4 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.

3.5 Ü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).

3.6 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.

3.6.1 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.

3.6.2 Beispiel

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);  
    }
...

3.6.3 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!
    }
...

4 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).

5 Quellen

  1. Kowarschick (MMProg): Wolfgang Kowarschick; Vorlesung „Multimedia-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2018; Quellengüte: 3 (Vorlesung)
  2. Musterlösung (Flash CS5)
  3. Musterlösung (Flash CS4)
  4. Erweiterte Musterlösung (Flash CS4)
  5. Erweiterte Musterlösung (Flash CS5)

5.1 SVN-Repository-Verweise