AS3-Tutorium: Flash: Butterfly 07b character: Unterschied zwischen den Versionen

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg
Kowa (Diskussion | Beiträge)
Die Seite wurde neu angelegt: „{{AS3-Tutorium:Flash:Butterfly:Menü}} {{In Bearbeitung}} =Weiterentwicklung der sechsten Version des Schmetterling-Movies= In [[AS3-Tutorium: Flash: Butterfly …“
 
Kowa (Diskussion | Beiträge)
Änderung 16922 von Kowa (Diskussion) rückgängig gemacht.
 
(4 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
{{AS3-Tutorium:Flash:Butterfly:Menü}}
{{AS3-Tutorium:Flash:Butterfly:Menü}}
{{In Bearbeitung}}


=Weiterentwicklung der sechsten Version des Schmetterling-Movies=
==Das Schmetterlingssymbol als Spielfigur mit weichen Übergängen==
 
In [[AS3-Tutorium: Flash: Butterfly 06 external code]] wurde der Code aus den Zeitleisten des Hauptfilms sowie der Symbole weitestgehend in Klassen ausgelagert.
Allerdings wurde bei der Implementierung bislang nicht das Prinzip [[Separation of Concerns]] (siehe auch [[Programmierprinzipien]]) beachtet.
Um dies zu erreichen, werden in einem ersten Schritt
die Symbole <code>Butterfly</code> und <code>ButterflyMovie</code> in „[[Spielfigur]]en“ (characters) umgewandelt.
 
Eine Spielfigur kann verschiedene Zustände einnehmen, wie z.B. „stehen“, „laufen nach links“, „laufen nach rechts“, „hüpfen“ etc.
Dabei sollte ein Figur-Objekt nicht selbst entscheiden, wann es in welchen Zustand übergeht. Die wird von außen (z.B.) über eine Logik-Komponente gesteuert.
Das heißt, eine Figur hat nur noch eine einzige Aufgabe: Den Zustand dieser Figur (der im Allgemeinen in einem Datenobjekt gespeichert wird) zu ''visualisieren''.


Im Folgenden wird zunächst das Symbol <code>Butterfly</code> in eine Spielfigur (im zuvor beschrieben Sinn) umgewandelt, um das Grundprinzip klar zu machen.
<swf width="367" height="267">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07b_character/Butterfly07bFlash11.swf</swf>
In zwei weiteren Schritten wird die Implementierung verbessert, so dass die Übergänge von einem Zustand in den anderen „weich“ erfolgen.
'''[http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07b_character/ Musterlösung (Flash CS5)]'''
Auf die zur Verfügung gestellte Schnittstelle hat dies keine Auswirkungen.
([http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs5/butterfly/butterfly_07b_character/ SVN-Repository])


Danach wird das Symbol <code>ButterflyMovie</code> in eine „Figur“ umgewandelt.
'''[http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs4/butterfly/butterfly_07b_character/ Musterlösung (Flash CS4)]'''
Hierbei wird insbesondere die Progammlogik (fliegen einer via <code>roundsToFly</code> vorgegebenen Anzahl von Runden)
([http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs4/butterfly/butterfly_07b_character/ SVN-Repository])
aus der Klasse <code>ButterflyMovie</code> in die Hauptklasse <code>Main</code> verlagert.
<noinclude>
 
<swf width="367" height="267">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/Butterfly07Flash11.swf</swf>
'''[http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/ Musterlösung (Flash CS5)]'''
([http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs5/butterfly/butterfly_07_character/ SVN-Repository])
 
'''[http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs4/butterfly/butterfly_07_character/ Musterlösung (Flash CS4)]'''
([http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs4/butterfly/butterfly_07_character/ SVN-Repository])
</noinclude>
</noinclude>


==Das Datenmodell==
Einen Nachteil hat die im [[AS3-Tutorium: Flash: Butterfly 07a character|Teil 7a]] angegebene Implementierung des Schmetterlingssymbols noch.  
 
Ein Übergang von einem Zustand zu einem anderen erfolgt im Allgemeinen ruckartig.
Bevor mit der Programmierung einer Anwendung begonnen wird, sollte zunächst ein [[Datenmodell]]
([[Klassendiagramm (UML)|Klassendiagramm]], [[Zustandsdiagramm (UML)|Zustandsdiagramm]] etc.)
erstellt werden. Folgendes Klassendiagramm wird in diesem Teil des Tutorium realisiert:
 
[[Medium:AS3-Tutorium Butterfly Flash 07 Class Diagram.jpg]]
 
'''[http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/uml/ Ordner mit StarUML-Diagramm und von StarUML erzeugtem AS3-Code-Rahmen]'''
([http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs5/butterfly/butterfly_07_character/uml/ SVN-Repository])
 
 
Es gibt zwei Klassen zur Visualisierung einer Spielfigur (so genannte [[Model-View-Controller-Paradigma|View]]-Klassen):
 
* <code>Butterfly</code>, eine Spielfigur, die sich nicht selbstständig vom Platz bewegt, aber verschiedene Zustände annehmen kann.
* <code>ButterflyMovie</code>, eine animierte Spielfigur, die sich auf vordefinierten Bahnen bewegt und auch verschiedene Zustände annehmen kann.
 
Beide Klassen sind, da sie [[Symbol (Flash)|Symbol]]en zugeordnet werden, Subklassen der ActionScript-Klasse <code>MovieClip</code>.
 
Ein Schmetterling kann drei Zustände annehmen:  
* Flügel offen (<code>OPEN</code>)
* Flügel geschlossen (<code>CLOSE</code>)
* flatternd (<code>FLUTTERING</code>)
 
Dieser Zustand kann über das Attribut <code>state</code> gelesen und modifiziert werden.
Mit Hilfe des Attributs <code>stateAnimation</code> kann erfragt werden, in welchem Zustand sich die
Animation gerade befindet (Flügel offen oder geschlossen). Dies ändert sich insbesondere während des Flatterns
ständig.
 
Der Schmetterling informiert mittels [[Signal]]en ([[Event]]s) interessierte [[Beobachter]] ([[Observers]], [[Event Listener]]s) sowohl
dann, wenn sich der Zustand (<code>state</code>) ändert, als auch dann, wenn sich der Zustand der Animation
(<code>stateAnimation</code>), d.h. der Flügel ändert. Dazu wird die Signal-Klasse <code>ButterflyEvent</code>
(eine Subklasse der ActionScript-Klasse <code>Event</code>) definiert, die zwei Konstanten zur Beschreibung dieser Ereignistypen bereithält.
 
Ein Schmetterlingsmovie kann vier Zustände annehmen:
* wartend (<code>WAITING</code>)
* herumfliegend (<code>FLYING_AROUND</code>)
* wegfliegend (<code>FLYING_AWAY</code>)
* von außerhalb zum Warteplatz fliegend (<code>FLYING_IN</code>)
 
Dieser Zustand kann über das Attribut <code>state</code> gelesen und modifiziert werden.
Mit Hilfe des Attributs <code>stateAnimation</code> kann erfragt werden, in welchem Zustand sich die
Animation gerade befindet. Dieses Attribut ändert sich jeweils zum Ende
einer Flugsequenz. Das heißt, das Attribut kann insgesamt sieben Werte annehmen:
 
* wartend (<code>WAITING</code>)
* herumfliegen wurde beendet (<code>END_FLYING_AROUND</code>)
* wegfliegen wurde beendet (<code>END_FLYING_AWAY</code>)
* zum Warteplatz fliegen wurde beendet (<code>END_FLYING_IN</code>)
 
Der Schmetterlingsmovie informiert ebenfalls mittels Signalen interessierte Beobachter sowohl
dann, wenn sich der Zustand (<code>state</code>) ändert, als auch dann, wenn sich der Zustand der Animation
(<code>stateAnimation</code>) ändert, d.h., wenn eine Flugsequenz beendet wird. Hierfür wird die Klasse
<code>ButterflyMovieEvent</code> verwendet.
 
Auf das Schmetterlingsobjekt, dass dem Schemtterlingsmovie zugeordnet ist, kann von außen nicht zugegriffen werden:
Die Beziehung wurde als <code>private</code> definiert.
Wäre ein Direktzugriff möglich, so könnte man z.B. das Flügelschlagen während des Rundfluges deaktivieren.
Außerdem würde der Direktzugriff Verletzungen des [[Gesetz von Demeter|Gesetzes von Demeter]] ermöglichen.
 
Als Ersatz für den fehlenden Direkzugriff werden zwei weitere Attribute angeboten:
 
* <code>butterflyState</code>: definiert den Fügelzustand für den wartenden Schmetterling (offen, geschlossen, flatternd); wenn er fliegt, flatter er immer
* <code>butterflyStateAnimation</code>: gibt den aktuellen Flügelzustand preis: offen oder geschlossen
 
Da auf das Schmetterlingsobjekt nicht direkt zugegriffen werden kann, signalisiert der Schmetterlingsmovie auch die
Zustandsänderungen des Schmetterlingsobjektes.
 
Hier sieht man ein Problem, das sich bei der Beachtung des Gesetzes von Demeter ergibt, sehr deutlich.
Ein Objekt, das keinen Direktzugriff auf ein ihm zugeordnetes Objekt ermöglicht, muss als [[Adapter]] ([[Wrapper]])
für dieses Objekt implementiert werden. Dies hat zur Folge, dass zusätzlicher Code geschrieben werden muss, der im
Wesentlichen nichts weiter macht, als [[Nachricht (OOP)|Nachricht]]en weiterzuleiten.
 
Hier hat diese Vorgehen allerdings einen entscheidenden Vorteil: Es ist dem Benutzer der Klasse <code>ButterflyMovie</code>
nicht möglich, das Verhalten des Schmetterling in einem unpassenden Augenblick (d.h. während des Fluges)
zu verändern.
 
Im Allgemeinen ist Code, der hilft, Programmierfehler zu vermeiden, Code vorzuziehen, der
aufgrund des geringeren Overheads etwas effizienter ist.
 
==Das Schmetterlingssymbol als Spielfigur==
 
Die Grundidee ist folgende: Für jeden Zustand der Spielfigur wird im Movie Clip ein eigener Abschnitt (d.h. ein zusammenhängender Bereich von Frames) definiert.
 
Das erste Frame eines jeden Abschnittes wird mit einen Label versehen, dessen Name sich aus dem zugehörigen Zustand ableitet  (z.B. <code>lb_</code>'''<code>x</code>''', wobei '''<code>x</code>''' durch den aktuellen Zustand ersetzt wird).
 
Das letzte Frame eines jeden Abschnittes wird mit einem ActionScript-Befehl versehen:
* <code>stop();</code>, falls die dem Zustand zugeordnete Animation  nur einmal abgespielt werden soll
* <code>gotoAndPlay(lb_</code>'''<code>x</code>'''<code>);</code>, falls die dem Zustand zugeordnete Animation dauerhaft abgespielt werden soll
 
Zunächst wird das Schmetterlingssymbol auf diese Weise umgestaltet.
 
# In der Bibliothek: Das Symbol <code>Butterfly</code> mit einem Doppelklick öffnen.
# In der Zeitleiste: Zwei neue Ebenen (ganz oben) einfügen:
#* <code>scripts</code>
#* <code>labels</code>
# In der Zeitleiste: Alle Ebenen (und damit alle Frames) markieren und den Block mit allen Frames nach rechts verschieben, so dass das erste Schlüsselbild auf Frame 40 liegt.
# In der Zeitleiste, Ebene <code>body</code>: Klick auf das Schlüsselbild bei Frame 40 → <code>Alt</code>-Taste drücken und festhalten → Linke Maustaste über Schlüsselbild bei Frame 40 drücken, festhalten und nach links bis Frame 1 verschieben ⇒ Der Schmetterlingskörper ist ab Frame 1 vorhanden; eer entsprechende Bereich in der Zeitleiste hat sich grau eingefärbt.
# In der Zeitleiste, Ebene <code>body</code>: Das Schlüssenbild bei Frame 40 entfernen: Klick auf das Schlüsselbild → Rechtsklick auf das Schlüsselbild → <code>Schlüsselbild löschen</code>.
# In der Zeitleiste, Ebene <code>wings</code>: Klick auf Frame 20 → Rechtsklick auf Frame 20 → <code>Schlüsselbild einfügen</code>.
# In der Zeitleiste, Ebene <code>wings</code>: <code>Strg</code>/<code>Ctrl</code>-Taste drücken und festhalten →  Klick auf das Schlüsselbild bei Frame 40 → Rechtsklick auf das Schlüsselbild → <code>Bilder kopieren</code>.
# In der Zeitleiste, Ebene <code>wings</code>: Klick auf Frame 1 → Rechtsklick auf Frame 1 → <code>Bilder einfügen</code> (nicht <code>Bild einfügen</code>!!).
# In der Zeitleiste, Ebene <code>wings</code>: <code>Strg</code>/<code>Ctrl</code>-Taste drücken und festhalten →  Klick auf das Schlüsselbild bei Frame 50 → Rechtsklick auf das Schlüsselbild → <code>Bilder kopieren</code>.
# In der Zeitleiste, Ebene <code>wings</code>: Klick auf Frame 20 → Rechtsklick auf Frame 20 → <code>Bilder einfügen</code> (nicht <code>Bild einfügen</code>!!).
# In der Zeitleiste, Ebene <code>labels</code>: Klick auf Frame 1 → Eigenschaften-Fenster öffnen → Name: <code>lb_open</code>
# In der Zeitleiste, Ebene <code>labels</code>: Klick auf Frame 20 → Rechtsklick auf Frame 20 → <code>Schlüsselbild einfügen</code> → Eigenschaften-Fenster, Name: <code>lb_close</code>
# In der Zeitleiste, Ebene <code>labels</code>: Klick auf Frame 40 → Eigenschaften-Fenster, Name: <code>lb_fluttering</code>
# In der Zeitleiste, Ebene <code>scripts</code> folgenden Code eintragen (Schlüsselbilder nicht vergessen!):
#; Frame 10: <code>stop();</code>
#; Frame 39: <code>stop();</code>
#; Frame 59: <code>gotoAndPlay("lb_fluttering");</code>
 
Als nächstes wird der Code zur Steuerung des Zustandswechsels dem Schmetterlings-Symbol zugeordnet:
 
# Im Menü: <code>Datei</code> → <code>Neu</code> → <code>ActionScript 3.0-Klasse</code> (CS5) bzw.  <code>ActionScript-Datei</code> (CS4 und früher) → <code>OK</code><br/> Nur in CS5: -> Klassenname: <code>Butterfly</code> → <code>OK</code>
# Im Menü: <code>Datei</code> → <code>Datei speichern unter</code> → Im Ordner der <code>fla</code>-Datei folgende Unterordner anlegen: <code>hsa/tutorial/butterfly/view</code> → Datei unter dem Namen <code>Butterfly.as</code> im zuvor angelegten Unterordner speichern.
# In der Bilbliothek: Rechtsklick auf das Symbol <code>Butterfly</code> → <code>Eigenschaften</code> → falls notwendig: Klick auf Dreieck hinter <code>Erweitert</code> → Häckchen vor <code>Export für ActionScript</code> → Klasse: <code>hsa.tutorial.butterfly.view.ButterflyBlue</code> (oder eine andere Farbe), Basisklasse <code>hsa.tutorial.butterfly.view.Butterfly</code> → <code>OK</code> → <code>OK</code>
 
Nun kann in die Datei <code>Butterfly.as</code> folgendener Code einfügt werden:
<source lang="actionscript">
package hsa.tutorial.butterfly.view
{
  import flash.display.MovieClip;
 
  public class Butterfly extends MovieClip
  {
    /////////////////////////////////////////////////////////////////////////////
    // Constants
    /////////////////////////////////////////////////////////////////////////////
 
    public static const OPEN:      String = "open";
    public static const CLOSE:      String = "close";
    public static const FLUTTERING: String = "fluttering";
 
    /////////////////////////////////////////////////////////////////////////////
    // Instance variables
    /////////////////////////////////////////////////////////////////////////////
 
    private var v_state: String;
 
    ////////////////////////////////////////////////////////////////////////////
    // Attributes
    /////////////////////////////////////////////////////////////////////////////
   
    public function get state(): String
    {
      return v_state;
    }
   
    public function set state(p_state: String): void
    {
      if (p_state != v_state)
      {
        this.gotoAndPlay("lb_"+ p_state);
        v_state = p_state;
      };
    }
 
    /////////////////////////////////////////////////////////////////////////////
    // Constructor
    /////////////////////////////////////////////////////////////////////////////
   
    public function Butterfly(p_state: String = OPEN)
    {
      this.state = p_state;
    }
   
    /////////////////////////////////////////////////////////////////////////////
    // End of class
    /////////////////////////////////////////////////////////////////////////////
  }
}
</source>
 
===Signale===
Bei jeder Zustandsänderung soll das Schmetterlings-Objekt laut Klassendiagramm
alle interessierten Objekte, d.h. alle [[Observer-Pattern|Observer]] (bzw. in ActionScript: Event Listener)
mit Hilfe eines [[Signal]]s (in ActionScript [[Event]] genannt)
über die erfolgte Zustandsänderung informieren. Dies wäre
mit Hilfe des Befehls
<source lang="actionscript">
this.dispatchEvent(new Event(Event.CHANGE));
</source>
problemlos möglich.
 
Allerdings ist es sauberer, eine eigene Klasse <code>ButterflyEvent</code>
als Subklasse von <code>Event</code> zu definieren und dort spezielle <code>ButterflyEvent</code>-Konstanten einzuführen.
 
Die zugehörige Datei <code>ButterflyEvent.as</code> muss, wenn als Package-Name ebenfalls <code>hsa.tutorial.butterfly.event</code>
angegeben wird, im Unterordner <code>hsa/tutorial/butterfly/event</code> gespeichert werden.
Der Inhalt dieser Datei könnte folgendermaßen aussehen:
 
<source lang="actionscript">
package hsa.tutorial.butterfly.event
{
  import flash.events.Event;
 
  public class ButterflyEvent extends Event
  {
    /////////////////////////////////////////////////////////////////////////////
    // Constants
    /////////////////////////////////////////////////////////////////////////////
   
    public static const CHANGE_BUTTERFLY:          String
                          = "change butterfly";
    public static const CHANGE_BUTTERFLY_ANIMATION: String
                          = "change butterfly animation";
   
    /////////////////////////////////////////////////////////////////////////////
    // Constructor
    /////////////////////////////////////////////////////////////////////////////
   
    public function ButterflyEvent(type: String = CHANGE_BUTTERFLY)
    {
      super(type);
    }
   
    /////////////////////////////////////////////////////////////////////////////
    // End of class
    /////////////////////////////////////////////////////////////////////////////
  }
}
</source>
 
Sobald man diese Datei erstellt hat, kann man die Setter-Methode <code>set state</code>
in der Datei <code>Butterfly.as</code> erweitern:
 
<source lang="actionscript">
    public function set state(p_state: String): void
    {
      if (p_state != v_state)
      {
        this.gotoAndPlay("lb_"+ p_state);
        v_state = p_state;
        this.dispatchEvent(new ButterflyEvent());
      };
    }
</source>
 
Damit jede Änderung der Flügelstellung ebenfalls mit Hilfe eines Signals (der Art <code>CHANGE_STATE_ANIMATION</code> ) angezeigt wird, ist
ein wenig mehr Aufwand notwendig. In der Zeitleiste des Symbols <code>Butterfly</code> müssen geeignete Methodenaufrufe eingefügt werden:
 
# In der Zeitleiste des Symbols <code>Butterfly</code>, Ebene <code>scripts</code> folgenden Code eintragen (Schlüsselbilder nicht vergessen!):
#; Frame 1: <code>m_new_wings_state(OPEN);</code>
#; Frame 20: <code>m_new_wings_state(CLOSE);</code>
#; Frame 40: <code>m_new_wings_state(OPEN);</code>
#; Frame 50: <code>m_new_wings_state(CLOSE);</code>
 
Nun müssen noch die Methode <code>m_new_wings_state</code> sowie
das [[Attribut]] <code>stateAnimation</code>
in die Klasse <code>Butterfly</code> eingefügt werden. Man beachte, dass es
keine Setter-Methode gibt. Das Attribut kann von außen nur gelesen werden.
 
<source lang="actionscript">
    private var v_state_animation: String;
 
    public function get stateAnimation(): String
    {
      return v_state_animation;
    }
 
    // Should be private, but cannot as it is accessed from within the timeline!
    public function m_new_wings_state(p_state: String): void
    {
      v_state_animation = p_state;
      this.dispatchEvent
        (new ButterflyEvent(ButterflyEvent.CHANGE_BUTTERFLY_ANIMATION));
    }
</source>
 
Insgesamt sieht der Inhalt der Datei <code>Butterfly.as</code> nun folgendermaßen aus:
 
<source lang="actionscript">
package hsa.tutorial.butterfly.view
{
  import flash.display.MovieClip;
  import hsa.tutorial.butterfly.event.ButterflyEvent;
 
  public class Butterfly01 extends MovieClip
  {
    /////////////////////////////////////////////////////////////////////////////
    // Constants
    /////////////////////////////////////////////////////////////////////////////
   
    public static const OPEN:      String = "open";
    public static const CLOSE:      String = "close";
    public static const FLUTTERING: String = "fluttering";
   
    /////////////////////////////////////////////////////////////////////////////
    // Instance variables
    /////////////////////////////////////////////////////////////////////////////
   
    private var v_state:          String;
    private var v_state_animation: String;
 
    ////////////////////////////////////////////////////////////////////////////
    // Methods to be accessed from within the timeline!
    /////////////////////////////////////////////////////////////////////////////
   
    // Should be private, but cannot as it is accessed from within the timeline!
    public function m_new_wings_state(p_state: String): void
    {
      if (p_state != v_state_animation)
      {
        v_state_animation = p_state;     
        this.dispatchEvent
          (new ButterflyEvent(ButterflyEvent.CHANGE_BUTTERFLY_ANIMATION));
      };
    }
 
    ////////////////////////////////////////////////////////////////////////////
    // Attributes
    /////////////////////////////////////////////////////////////////////////////
   
    public function get state(): String
    {
      return v_state;
    }
   
    public function set state(p_state: String): void
    {
      if (p_state != v_state)
      {
        this.gotoAndPlay("lb_"+ p_state);
        v_state = p_state;
        this.dispatchEvent(new ButterflyEvent());
      };
    }
   
    public function get stateAnimation(): String
    {
      return v_state_animation;
    }
   
    /////////////////////////////////////////////////////////////////////////////
    // Constructor
    /////////////////////////////////////////////////////////////////////////////
   
    public function Butterfly(p_state: String = OPEN)
    {
      this.state = p_state;
    }
   
    /////////////////////////////////////////////////////////////////////////////
    // End of class
    /////////////////////////////////////////////////////////////////////////////
  }
}
</source>
 
Damit ist es z.B. möglich, einen Flügelschlag-Sound mit dem Movie zu synchronisieren.
Im nachfolgenden Beispiel wird der aktuelle Zustand des Flügels in einem zweiten Textfeld ausgegeben.
Ein drittes Textfeld simuliert den Flip-Flap-Sound (da mir derzeit leider kein echter Flügelschlag-Sound vorliegt).
 
<swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/Butterfly07Butterfly01Flash11.swf</swf>
 
Um diese Verhalten zu erreichen, wurde in der
Haupt-Timeline der zugehörigen <code>fla</code>-Datei folgender Code eingefügt
(da der Movie nur zum Testen der Klasse <code>Butterfly</code>
dient wurde der Code nicht ausgelagert):
 
<source lang="actionscript">
import hsa.tutorial.butterfly.event.ButterflyEvent;
import hsa.tutorial.butterfly.view.Butterfly;
import fl.transitions.Tween;
import fl.transitions.easing.None;
 
sp_butterfly.addEventListener(ButterflyEvent.CHANGE_BUTTERFLY,          o_change);
sp_butterfly.addEventListener(ButterflyEvent.CHANGE_BUTTERFLY_ANIMATION, o_wings);
 
function o_change(p_event: ButterflyEvent): void
{
  sp_label_state.text = sp_butterfly.state;
}
 
function o_wings(p_event: ButterflyEvent): void
{
  sp_label_wings.text = sp_butterfly.stateAnimation;
 
sp_label_flap.text = (sp_butterfly.stateAnimation == Butterfly.OPEN)
                      ? "flip"
                        : "flap";
  new Tween
(sp_label_flap, // object to be animated
    "alpha",      // animation attribute: "x", "y", "alpha", "rotation", ...
    None.easeNone, // animation type
    1.0, 0.0,      // start and end value (of alpha)
    15,            // Dauer der Animation ...
    false          // duration in frames (not in seconds)
  );
}
</source>
 
===Anmerkung 1===
Man beachte, dass im Gegensatz
zu den vorangegangen Tutorien die Labelbezeichnungen nicht
mehr <code>lbOpen</code>, <code>lbClose</code> und <code>lbFluttering</code>
lauten, sondern <code>lb_open</code>, <code>lb_close</code> und <code>lb_fluttering</code>.
 
Gemäß den hier verwendeten [[Multimedia-Programmierung: Konventionen|Namenskonventionen]]
bedeutet dies, dass von außen nicht mehr direkt auf die Zeitleistenlabels zugegriffen werden soll.
Sie werden als <code>private</code> angesehen (auch wenn dies in Flash nicht realisierbar ist).
Das heißt, auf die Labels wird nur noch innerhalb der Klasse <code>Butterlfy</code> zugegrifen
(konkret: in der Setter-Methode <code>set state</code>).
 
===Anmerkung 2===
Für den Schmetterling wurde eine saubere Schnittstelle definiert (die [[Attribut]]e <code>state</code> und <code>stateAnimation</code>
sowie die Ereignisse (Events) die vom Schmetterling gemeldet werden).
Nur über diese Schnittstelle darf von außen auf ein Schmetterlings-Objekt zugegriffen werden.
 
Wenn man einen Schmetterling
auf die Bühne legt und ihm den „Instanznamen“ <code>sp_butterfly</code> gibt,
kann man mit folgenden Befehlen dessen Zustand verändern:
 
<source lang="actionscript">
import hsa.tutorial.butterfly.view.Butterfly;
 
sp_butterfly.state=Butterfly.OPEN;
sp_butterfly.state=Butterfly.CLOSE;
sp_butterfly.state=Butterfly.FLUTTERING;
</source>
 
Außerdem kann man den aktuellen Zustand des Schmetterling und auch seiner Flügel jederzeit lesen.
Und man kann sich jederzeit über Zustandsänderungen informieren lassen.
 
==Das Schmetterlingssymbol als Spielfigur mit weichen Übergängen==
 
Einen Nachteil hat die vorangegangene Implementierung noch. Ein Übergang
von einem Zustand zu einem anderen erfolgt im Allgemeinen ruckartig.
Wenn man z.B. von CLOSE ind FLUTTERING wechselt, werden die Flügel
Wenn man z.B. von CLOSE ind FLUTTERING wechselt, werden die Flügel
schlagartig geöffnet.  
schlagartig geöffnet.  


Sowohl im ersten Beispielfilm, als auch im zweiten Beispielsfilm wird zunächst alle 30 Frames und danach kurzeitig viel häufiger der Zustand des Schmetterlings geändert. Man beachte den Unterschied.
In beiden folgenden Filmen (links: Lösung gemäß Teil 7a, rechts: Lösung gemäß Teil 7b)
wird zunächst alle 30 Frames und danach kurzeitig viel häufiger der Zustand des Schmetterlings geändert. Man beachte den Unterschied.


<swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/Butterfly07ButterflyRoughFlash11.swf</swf><swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/Butterfly07ButterflySmoothFlash11.swf</swf>
{|
| <swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07a_character/Butterfly07aProblemFlash11.swf</swf>  
| <swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07b_character/Butterfly07bFlash11.swf</swf>
|}


Um das Problem mit den ruckartigen Zustandsübergängen zu beheben, muss der flatternde Schmetterling regelmäßig melden, wenn seine
Um das Problem mit den ruckartigen Zustandsübergängen zu beheben, muss der flatternde Schmetterling regelmäßig melden, wenn seine
Zeile 529: Zeile 86:
Datei <code>Butterfly.as</code> wie folgt:
Datei <code>Butterfly.as</code> wie folgt:


<source lang="actionscript">
<source lang="actionscript3">
/////////////////////////////////////////////////////////////////////////////////
package hsa.tutorial.butterfly.view
// Copyright © 2010  W. Kowarschick
//
// Dieses Werk darf unter den Bedingungen der Creative Common Lizenz by-nc 3.0 de
//  (http://creativecommons.org/licenses/by-nc/3.0/de/)
// vervielfaeltigt, verbreitet, publiziert und modifiziert werden.
/////////////////////////////////////////////////////////////////////////////////
 
package hsa.tutorial.butterfly.hsa
{
{
   import flash.display.MovieClip;
   import flash.display.MovieClip;
Zeile 548: Zeile 97:
     // Constants
     // Constants
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
 
   
     public static const OPEN:      String = "open";
     public static const OPEN:      String = "open";
     public static const CLOSE:      String = "close";
     public static const CLOSE:      String = "close";
Zeile 555: Zeile 104:
     private static const c_label_prefix:            String = "lb_";
     private static const c_label_prefix:            String = "lb_";
     private static const c_label_prefix_fluttering: String = "lb_fluttering_";
     private static const c_label_prefix_fluttering: String = "lb_fluttering_";
 
   
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
     // Instance variables
     // Instance variables
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
 
   
     // Instance variables of the attributes
     // Instance variables of the attributes
     private var v_state:         String;
     private var v_state:           String;
    private var v_state_animation: String;
      
      
     // Auxiliary variable (for being able to perform delayed state changes)
     // Auxiliary variable (for being able to perform delayed state changes)
     private var v_next_state: String = null;
     private var v_next_state: String = null;
 
   
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
     // Private methods
     // Private methods
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
      
 
     // Should be private, but cannot as it is accessed from within the timeline!
    public function m_new_wings_state(p_state: String): void
    {
      if (p_state != v_state_animation)
      {
        v_state_animation = p_state;     
        // The state of the wings has changed. Inform the observers!
        this.dispatchEvent
          (new ButterflyEvent(ButterflyEvent.CHANGE_BUTTERFLY_ANIMATION));
      };
 
      // If the wings are in the correct position,
      // perform the delayed state change!
      if (v_next_state == p_state)
      { 
        this.gotoAndPlay(c_label_prefix + v_next_state);
        m_set_state(v_next_state); // Make up for the state change now!
        v_next_state = null;
      };
    }
 
     private function m_set_state(p_state: String): void
     private function m_set_state(p_state: String): void
     {
     {
      trace(v_state + " ===> " + p_state);
     
       if (v_state != p_state)
       if (v_state != p_state)
       {
       {
Zeile 582: Zeile 155:
     // Attributes
     // Attributes
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
 
   
     /**
     /**
     * The current state of the butterfly:  
     * The current state of the butterfly:  
Zeile 593: Zeile 166:
      
      
     public function set state(p_state: String): void
     public function set state(p_state: String): void
     {    
     {
      trace(v_state + " ---> " + p_state);
     
       if (p_state != OPEN && p_state != CLOSE && p_state != FLUTTERING)
       if (p_state != OPEN && p_state != CLOSE && p_state != FLUTTERING)
         throw (new Error("ButterflySmooth: State '" + p_state + "' is unknown!"));
         throw (new Error("Butterfly: State '" + p_state + "' is unknown!"));
 
     
       if (v_next_state == null) // Currently no delayed state change is performed!
       if (v_next_state == null) // Currently no delayed state change is performed!
       {  
       {  
         if (p_state == FLUTTERING)
         if (v_state != FLUTTERING)
        {
           this.gotoAndPlay(  c_label_prefix_fluttering
           this.gotoAndPlay(  c_label_prefix_fluttering  
                           + ((v_state == null) ? OPEN : v_state)
                           + ((v_state == null) ? OPEN : v_state)
                           );
                           );
        if (p_state == FLUTTERING)
           m_set_state(p_state); // Do the state change now!
           m_set_state(p_state); // Do the state change now!
        }
         else // p_state == OPEN || CLOSE
         else // p_state == OPEN || CLOSE
        {
           v_next_state = p_state; // Delay the stage change.
           if (v_state != FLUTTERING)
            this.gotoAndPlay(  c_label_prefix_fluttering
                            + ((v_state == null) ? OPEN : v_state)
                            );
         
          // Delay the stage change until the wings are in the correct position.
          v_next_state = p_state;
        };
       }
       }
       else // Currently a delayed state change is performed!
       else // Currently a delayed state change is performed!
Zeile 629: Zeile 196:
     }
     }
      
      
     /////////////////////////////////////////////////////////////////////////////
     /**
    // Methods called from within the timline (should be private, but cannot)
    * The current state of the wings:
    /////////////////////////////////////////////////////////////////////////////
    *  <code>OPEN</code> or <code>CLOSE</code>.
   
    */
     public function m_new_wings_state(p_state: String): void
     public function get stateAnimation(): String
     {
     {
       // If the wings are in the correct position,
       return v_state_animation;
      // perform the delayed state change!
      if (v_next_state == p_state)
      { 
        this.gotoAndPlay(c_label_prefix + v_next_state);
        m_set_state(v_next_state); // Make up for the state change now!
        v_next_state = null;
      };
     }
     }
   
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
     // Constructor
     // Constructor
Zeile 653: Zeile 213:
       this.state = p_state;
       this.state = p_state;
     }
     }
 
   
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
     // End of class
     // End of class
Zeile 661: Zeile 221:
</source>
</source>


===Anmerkung 1===
===Anmerkung===
Man beachte, dass hier nur eine verbesserte, wenn auch viel aufwändigere Implementierung des Symbols <code>Butterfly</code>
Man beachte, dass hier nur eine verbesserte, wenn auch viel aufwändigere Implementierung des Symbols <code>Butterfly</code>
angegeben wurde. An der zuvor definierten Schnittstellte ändert sich nichts.
angegeben wurde. An der zuvor definierten Schnittstellte ändert sich nichts.
Zeile 668: Zeile 228:
aufgerufen wird.
aufgerufen wird.


==Das Schmetterlingssymbol als Spielfigur mit weichen Übergängen: Vereinfachte Version==
Der Code, der im vorangegangenen Abschnitt erstellt wurde, ist ziemlich umfangreich und schwer zu verstehen.
Wenn Spielfiguren komplexere Bewegungsabläufe aufweisen, ist es i. Allg. noch schwieriger, einen weichen Übergang von Zustand zu Zustand
zu realisieren. Beispielsweise dreht sich daher eine Spielfigur eines Jump'n'Run-Spiels so gut wie immer schlagartig um 180 Grad,
wenn der Benutzer die Bewegungsrichtung ändert.
Die Kompelxität der zuvor definierten <code>Butterfly</code>-Klasse ist vor allem der Tatsache geschuldet,
dass für jeden Zustand eine eigener Frames-Block definiert wurde. Obwohl dies i.Allg. notwendig ist
(und daher dieser allgemeine Fall auch im Tutorium demonstriert wurde), soll nicht verschwiegen werden,
dass es für den Schmetterling eine wesentlich einfache Implementierung gibt, die genau dasselbe Verhalten wie
zuvor realisiert.
Im dritten Frame-Block sind alle gewünschten Zustände enthalten:
Flügelschlagender Schmetterling, Schmetterling mit offenen Flügeln und Schmetterling mit geschlossenen Flügeln.
Dehalb reicht es, den Flügelschlag im richtigen Moment anzuhalten bzw. zu starten, um zwischen den einzelen Zuständen zu wechseln.
Ein Sprung über mehrer Frames hinweg ist (außer beim Rücksprung auf das erste Bild der Framesequenz) gar nicht notwendig.
Folgende Vereinfachungen müssen zunächst am Schmetterling vorgenommen werden:
# In der Zeitleiste des Symbols <code>Butterfly</code> werden die beiden zuvor eingeführten zusätzlichen Frame-Blöcke wieder entfernt: Die Frames 1 bis 39 in allen vier Ebenen gleichzeitig markieren → Rechtsklick in den markierten Bereich → <code>Bilder entfernen</code>.
# In der Zeitleiste, Ebene <code>labels</code>: Die Label <code>lb_butterfly_open</code> und <code>lb_butterfly_clos</code> in <code>lb_open</code> und <code>lb_close</code> umbenennen.
# In der Zeitleiste, Ebene <code>scripts</code>, Frame 21: <code>gotoAndPlay("lb_open");</code>  an Stelle von <code>gotoAndPlay("lb_fluttering_open");</code>
Den Code in der Datei <code>Butterfly.as</code> durch folgenden Code ersetzen:
<source lang="actionscript">
package hsa.tutorial.butterfly.view
{
  import flash.display.MovieClip;
  import hsa.tutorial.butterfly.event.ButterflyEvent;
 
  public class Butterfly extends MovieClip
  {
    /////////////////////////////////////////////////////////////////////////////
    // Constants
    /////////////////////////////////////////////////////////////////////////////
    public static const OPEN:      String = "open";
    public static const CLOSE:      String = "close";
    public static const FLUTTERING: String = "fluttering";
    /////////////////////////////////////////////////////////////////////////////
    // Instance variables
    /////////////////////////////////////////////////////////////////////////////
    // Instance variables of the attributes
    private var v_state: String;
   
    // Auxiliary variable (for being able to perform delayed state changes)
    private var v_stop_state: String = null;
    /////////////////////////////////////////////////////////////////////////////
    // Private methods
    /////////////////////////////////////////////////////////////////////////////
   
    private function m_set_state(p_state: String): void
    {
      if (v_state != p_state)
      {
        v_state = p_state;
        this.dispatchEvent(new ButterflyEvent());
      }
    }
   
    ////////////////////////////////////////////////////////////////////////////
    // Attributes
    /////////////////////////////////////////////////////////////////////////////
    public function get state(): String
    {
      return v_state;
    }
   
    public function set state(p_state: String): void
    {
      if (p_state == FLUTTERING)
      {
        v_stop_state = null; // Don't stop fluttering!
        this.play();        // Start the movie (if it currently is stoped).
        m_set_state(p_state); // Do the state change now!
      }
      else if (p_state == OPEN || p_state == CLOSE)
      {
        v_stop_state = p_state;
        this.play(); // Start the movie (if it currently is stoped).
      }
      else
        throw (new Error("Butterfly: State '" + p_state + "' is unknown!"));
    }
   
    /////////////////////////////////////////////////////////////////////////////
    // Methods called from within the timline (should be private, but cannot)
    /////////////////////////////////////////////////////////////////////////////
   
    public function m_new_wings_state(p_state: String): void
    {
      // If the wings are to be stoped, stop them.
      if (v_stop_state == p_state)
      {
        this.stop();
        v_stop_state = null;
        m_set_state(p_state); // Do the state change now!
      };
    }
    /////////////////////////////////////////////////////////////////////////////
    // Constructor
    /////////////////////////////////////////////////////////////////////////////
   
    public function Butterfly(p_state: String = OPEN)
    {
      this.state = p_state;
    }
    /////////////////////////////////////////////////////////////////////////////
    // End of class
    /////////////////////////////////////////////////////////////////////////////
  }
}
</source>
===Anmerkung 1===
Beachten Sie, dass der Code viel kürzer, einfacher und verständlicher ist, als im vorangegangen Beispiel.
TBD
<swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/Butterfly07ButterflySimplifiedFlash11.swf</swf>
==Der Schmetterlings-Movie als Spielfigur==
=Probleme der Implementierung=
 
=Quellen=
=Quellen=
<noinclude>
<noinclude>
* [[Kowarschick, W.: Multimedia-Programmierung]]</noinclude>
* [[Kowarschick, W.: Multimedia-Programmierung]]</noinclude>
* [http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/ Musterlösung (Flash CS5)]
* [http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07b_character/ Musterlösung (Flash CS5)]
* [http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs4/butterfly/butterfly_07_character/ Musterlösung (Flash CS4)]
* [http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs4/butterfly/butterfly_07b_character/ Musterlösung (Flash CS4)]
* [http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character_multi/ Erweiterte Musterlösung (Flash CS4)]
* [http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07b_character_multi/ Erweiterte Musterlösung (Flash CS4)]
* [http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs4/butterfly/butterfly_07_character_multi/ Erweiterte Musterlösung (Flash CS5)]
* [http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs4/butterfly/butterfly_07b_character_multi/ Erweiterte Musterlösung (Flash CS5)]


== [[Subversion/HowTo|SVN]]-Repository-Verweise ==
== [[Subversion/HowTo|SVN]]-Repository-Verweise ==
* [http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs5/butterfly/butterfly_07_character/ Musterlösung (Flash CS5)]
* [http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs5/butterfly/butterfly_07b_character/ Musterlösung (Flash CS5)]
* [http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs4/butterfly/butterfly_07_character/ Musterlösung (Flash CS4)]
* [http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs4/butterfly/butterfly_07b_character/ Musterlösung (Flash CS4)]
* [http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs5/butterfly/butterfly_07_character_multi/ Erweiterte Musterlösung (Flash CS4)]
* [http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs5/butterfly/butterfly_07b_character_multi/ Erweiterte Musterlösung (Flash CS4)]
* [http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs4/butterfly/butterfly_07_character_multi/ Erweiterte Musterlösung (Flash CS5)]<noinclude>[[Kategorie: AS3-Tutorium: Flash: Butterfly]][[Kategorie: Flash-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]
* [http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs4/butterfly/butterfly_07b_character_multi/ Erweiterte Musterlösung (Flash CS5)]<noinclude>[[Kategorie: AS3-Tutorium: Flash: Butterfly]][[Kategorie: Flash-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]]
{{{{SITENAME}}-konformer Artikel}}
{{{{SITENAME}}-konformer Artikel}}
</noinclude>
</noinclude>

Aktuelle Version vom 18. April 2011, 06:03 Uhr

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

Das Schmetterlingssymbol als Spielfigur mit weichen Übergängen

<swf width="367" height="267">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07b_character/Butterfly07bFlash11.swf</swf> Musterlösung (Flash CS5) (SVN-Repository)

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


Einen Nachteil hat die im Teil 7a angegebene Implementierung des Schmetterlingssymbols noch. Ein Übergang von einem Zustand zu einem anderen erfolgt im Allgemeinen ruckartig. Wenn man z.B. von CLOSE ind FLUTTERING wechselt, werden die Flügel schlagartig geöffnet.

In beiden folgenden Filmen (links: Lösung gemäß Teil 7a, rechts: Lösung gemäß Teil 7b) wird zunächst alle 30 Frames und danach kurzeitig viel häufiger der Zustand des Schmetterlings geändert. Man beachte den Unterschied.

<swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07a_character/Butterfly07aProblemFlash11.swf</swf> <swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07b_character/Butterfly07bFlash11.swf</swf>

Um das Problem mit den ruckartigen Zustandsübergängen zu beheben, muss der flatternde Schmetterling regelmäßig melden, wenn seine Flügel ganz offen oder geschlossen sind. Außerdem muss es möglich sein, im „Fluttering-Movie“ sowohl zu dem Frame, an dem die Flügel ganz geöffnet sind, als auch zu dem Frame, an dem die Flügel ganz geschlossen sind, zu springen. Dazu sind folgende Änderungen am Symbol Butterfly notwendig:

  1. In der Zeitleiste, Ebene labels: Labelnamen von Frame 40 in lb_fluttering_open abändern.
  2. In der Zeitleiste, Ebene labels: Folgenden Labelnamen in Frame 50 einfügen lb_fluttering_close.
  3. In der Zeitleiste, Ebene scripts;
    Frame 59
    gotoAndPlay("lb_fluttering_open"); // an Stelle von gotoAndPlay("lb_fluttering");

Die Implementierung der Setter-Methode set state ist nun allerdings sehr viel aufwändiger. Zunächst gibt es sechs Fälle, die berücksichtigt werden müssen. Den Fall dass sich alter und neuer Zustand nicht unterscheiden, braucht man (zunächst!) nicht zu betrachten.

Der einfachste Fall ist, dass der Schmetterling sich nicht bewegt und mit dem Flattern beginnen soll:

Aktueller Zustand Neuer Zustand Aktion
OPEN FLUTTERING gotoAndPlay("lb_fluttering_open") ⇒ der neue Status wird sofort erreicht
CLOSE FLUTTERING gotoAndPlay("lb_fluttering_close") ⇒ der neue Status wird sofort erreicht

In den übrigen vier Fällen muss der Statuswechsel verzögert werden:

Aktueller Zustand Neuer Zustand Aktion
OPEN CLOSE gotoAndPlay("lb_fluttering_open")
Aufruf von m_new_wings_state(CLOSE) ⇒ neuer Status ist erreicht
CLOSE OPEN gotoAndPlay("lb_fluttering_close")
Aufruf von m_new_wings_state(OPEN) ⇒ neuer Status ist erreicht
FLUTTERING CLOSE Aufruf von m_new_wings_state(CLOSE) ⇒ neuer Status ist erreicht
FLUTTERING OPEN Aufruf von m_new_wings_state(OPEN) ⇒ neuer Status ist erreicht

Leider reicht es nicht, diese sechs Fälle zu betrachten. Der Zustand des Schmetterlings kann ja erneut geändert werden, während sich dieser bereits in einer verzögerten Zustandsänderung befindet und daher zurzeit flattert. (Dies ist ein ganzt typisches Problem bei der Entwicklung von Multimedia-Anwendungen: Der Benutzer oder andere Komponenten können zu jeder Zeit Zustandsänderungen veranlassen, auch wenn die Anwendung eine andere Aufgabe noch nicht vollständig erledigt hat.)

Folgende zusätzlichen Fälle müssen betrachtet werden:

Aktueller Zustand Neuer Zustand Aktion
irgendein Zustand FLUTTERING Der Schmetterling hat den neuen Zustand bereits erreicht, da er bereits flattert.
Die verzögerte Zustandsänderung muss(!) unterbrochen werden.
OPEN oder
CLOSE
CLOSE Aufruf von m_new_wings_state(CLOSE) ⇒ neuer Status ist erreicht
OPEN oder
CLOSE
OPEN Aufruf von m_new_wings_state(OPEN) ⇒ neuer Status ist erreicht

Wenn man die obigen Tabellen in Code umsetzt, ändert sich der Inhalt der Datei Butterfly.as wie folgt:

package hsa.tutorial.butterfly.view
{
  import flash.display.MovieClip;
  import hsa.tutorial.butterfly.event.ButterflyEvent;
  
  public class Butterfly extends MovieClip
  {
    /////////////////////////////////////////////////////////////////////////////
    // Constants
    /////////////////////////////////////////////////////////////////////////////
    
    public static const OPEN:       String = "open";
    public static const CLOSE:      String = "close";
    public static const FLUTTERING: String = "fluttering";
    
    private static const c_label_prefix:            String = "lb_";
    private static const c_label_prefix_fluttering: String = "lb_fluttering_";
    
    /////////////////////////////////////////////////////////////////////////////
    // Instance variables
    /////////////////////////////////////////////////////////////////////////////
    
    // Instance variables of the attributes
    private var v_state:           String;
    private var v_state_animation: String;
    
    // Auxiliary variable (for being able to perform delayed state changes)
    private var v_next_state: String = null;
    
    /////////////////////////////////////////////////////////////////////////////
    // Private methods
    /////////////////////////////////////////////////////////////////////////////

    // Should be private, but cannot as it is accessed from within the timeline!
    public function m_new_wings_state(p_state: String): void
    {
      if (p_state != v_state_animation)
      {
        v_state_animation = p_state;      
        // The state of the wings has changed. Inform the observers!
        this.dispatchEvent
          (new ButterflyEvent(ButterflyEvent.CHANGE_BUTTERFLY_ANIMATION));
      };

      // If the wings are in the correct position,
      // perform the delayed state change!
      if (v_next_state == p_state)
      {  
        this.gotoAndPlay(c_label_prefix + v_next_state);
        m_set_state(v_next_state); // Make up for the state change now!
        v_next_state = null;
      };
    }

    private function m_set_state(p_state: String): void
    {
      trace(v_state + " ===> " + p_state);
      
      if (v_state != p_state)
      {
        v_state = p_state;
        this.dispatchEvent(new ButterflyEvent());
      }
    }
    
    ////////////////////////////////////////////////////////////////////////////
    // Attributes
    /////////////////////////////////////////////////////////////////////////////
    
    /**
     * The current state of the butterfly: 
     *   <code>OPEN</code>, <code>CLOSE</code> or <code>BUTTERFLY</code>.
     */
    public function get state(): String
    {
      return v_state;
    }
    
    public function set state(p_state: String): void
    {
      trace(v_state + " ---> " + p_state);
      
      if (p_state != OPEN && p_state != CLOSE && p_state != FLUTTERING)
        throw (new Error("Butterfly: State '" + p_state + "' is unknown!"));
      
      if (v_next_state == null) // Currently no delayed state change is performed!
      { 
        if (v_state != FLUTTERING)
          this.gotoAndPlay(  c_label_prefix_fluttering
                           + ((v_state == null) ? OPEN : v_state)
                          );

        if (p_state == FLUTTERING)
          m_set_state(p_state); // Do the state change now!
        else // p_state == OPEN || CLOSE
          v_next_state = p_state; // Delay the stage change.
      }
      else // Currently a delayed state change is performed!
      {
        if (p_state == FLUTTERING)
        {
          v_next_state = null;  // Stop the delayed state change.
          m_set_state(p_state); // Do the state change now!
        }
        else
          v_next_state = p_state; // Update the delayed state change;
      }
    }
    
    /**
     * The current state of the wings: 
     *   <code>OPEN</code> or <code>CLOSE</code>.
     */
    public function get stateAnimation(): String
    {
      return v_state_animation;
    }
    
    /////////////////////////////////////////////////////////////////////////////
    // Constructor
    /////////////////////////////////////////////////////////////////////////////
    
    public function Butterfly(p_state: String = OPEN)
    {
      this.state = p_state;
    }
    
    /////////////////////////////////////////////////////////////////////////////
    // End of class
    /////////////////////////////////////////////////////////////////////////////
  }
}

Anmerkung

Man beachte, dass hier nur eine verbesserte, wenn auch viel aufwändigere Implementierung des Symbols Butterfly angegeben wurde. An der zuvor definierten Schnittstellte ändert sich nichts. Man kann sich allerdings nicht mehr darauf verlassen, dass die Zustandswechsel sofort in dem Moment angezeigt wird, in dem die Setter-Methode set state aufgerufen wird.

Quellen

SVN-Repository-Verweise


Dieser Artikel ist GlossarWiki-konform.