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

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg
Keine Bearbeitungszusammenfassung
 
(29 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
{{AS3-Tutorium:Flash:Butterfly:Menü}}
{{AS3-Tutorium:Flash:Butterfly:Menü}}
{{In Bearbeitung}}


=Datenmodell=
=Weiterentwicklung der sechsten Version des Schmetterling-Movies=
<noinclude>


FEHLT NOCH
<swf width="367" height="267">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07a_character/Butterfly07aFlash11.swf</swf>
'''[http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07a_character/ Musterlösung (Flash CS5)]'''
([http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs5/butterfly/butterfly_07a_character/ SVN-Repository])


=Weiterentwicklung der sechsten Version des Schmetterling-Movies=
'''[http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs4/butterfly/butterfly_07a_character/ Musterlösung (Flash CS4)]'''
([http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs4/butterfly/butterfly_07a_character/ SVN-Repository])
</noinclude>


In [[AS3-Tutorium: Flash: Butterfly 06 external code]] wurde der Code aus den Zeitleisten des Hauptfilms sowie der Symbole weitestgehend in Klassen ausgelagert.
In [[AS3-Tutorium: Flash: Butterfly 06 external code]] wurde der Code aus den Zeitleisten des Hauptfilms sowie der Symbole weitestgehend in Klassen ausgelagert.
Zeile 18: Zeile 22:


Im Folgenden wird zunächst das Symbol <code>Butterfly</code> in eine Spielfigur (im zuvor beschrieben Sinn) umgewandelt, um das Grundprinzip klar zu machen.
Im Folgenden wird zunächst das Symbol <code>Butterfly</code> in eine Spielfigur (im zuvor beschrieben Sinn) umgewandelt, um das Grundprinzip klar zu machen.
In zwei weiteren Schritten wird die Implementierung verbessert, so dass die Übergänge von einem Zustand in den anderen „weich“ erfolgen.
In zwei weiteren Schritten ([[AS3-Tutorium: Flash: Butterfly 07b character|Teil 7b]], [[AS3-Tutorium: Flash: Butterfly 07c character|Teil 7c]]) wird die Implementierung verbessert, so dass die Übergänge von einem Zustand in den anderen „weich“ erfolgen.
Auf die zur Verfügung gestellte Schnittstelle hat dies keine Auswirkungen.
Auf die zur Verfügung gestellte Schnittstelle hat dies keine Auswirkungen.


Danach wird das Symbol <code>ButterflyMovie</code> in eine „Figur“ umgewandelt.
Zu guter Letzt ([[AS3-Tutorium: Flash: Butterfly 8 character|Teil 8]]) wird das Symbol <code>ButterflyMovie</code> ebenfalls in eine „Spielfigur“ umgewandelt.
Hierbei wird insbesondere die Progammlogik (fliegen einer via <code>roundsToFly</code> vorgegebenen Anzahl von Runden)
 
aus der Klasse <code>ButterflyMovie</code> in die Hauptklasse <code>Main</code> verlagert.
==Das Datenmodell==
<noinclude>
 
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:
 
[[Datei: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 eine Klasse zur Visualisierung einer Spielfigur (eine so genannte [[Model-View-Controller-Paradigma|View]]-Klasse):
 
* <code>Butterfly</code>, eine Spielfigur, die sich nicht vom Platz bewegt, aber verschiedene Zustände annehmen kann.
 
Diese Klasse ist, da sie [[Symbol (Flash)|Symbol]]en zugeordnet wird, 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>)


<swf width="367" height="267">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/Butterfly07Flash11.swf</swf>
Dieser Zustand kann über das Attribut <code>state</code> gelesen und modifiziert werden.
'''[http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/ Musterlösung (Flash CS5)]'''
Mit Hilfe des Attributs <code>stateAnimation</code> kann erfragt werden, in welchem Zustand sich die
([http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs5/butterfly/butterfly_07_character/ SVN-Repository])
Animation gerade befindet (Flügel offen oder geschlossen). Dies ändert sich insbesondere während des Flatterns
ständig.


'''[http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs4/butterfly/butterfly_07_character/ Musterlösung (Flash CS4)]'''
Der Schmetterling informiert mittels [[Signal]]en ([[Event]]s) interessierte [[Beobachter]] ([[Observers]], [[Event Listener]]s) sowohl
([http://glossar.hs-augsburg.de/webdav/tutorium/flash_cs4/butterfly/butterfly_07_character/ SVN-Repository])
dann, wenn sich der Zustand (<code>state</code>) ändert, als auch dann, wenn sich der Zustand der Animation
</noinclude>
(<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.


==Das Schmetterlingssymbol als Spielfigur==
==Das Schmetterlingssymbol als Spielfigur==
Zeile 58: Zeile 83:
# 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>: <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>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_opened</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_closed</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>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!):
# In der Zeitleiste, Ebene <code>scripts</code> folgenden Code eintragen (Schlüsselbilder nicht vergessen!):
#; Frame 10:
#; Frame 19: <code>stop();</code>
#: <code>stop();</code>
#; Frame 39: <code>stop();</code>
#; Frame 39:
#; Frame 59: <code>gotoAndPlay("lb_fluttering");</code>
#: <code>stop();</code>
#; Frame 59:
#: <code>gotoAndPlay("lb_fluttering");</code>


Als nächstes wird der Code zur Steuerung des Zustandswechsels dem Schmetterlings-Symbol zugeordnet:
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>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</code> → Datei unter dem Namen <code>Butterfly.as</code> im zuvor angelegten Unterordner speichern.
# 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.ButterflyBlue</code> (oder eine andere Farbe), Basisklasse <code>hsa.tutorial.butterfly.Butterfly</code> → <code>OK</code> → <code>OK</code>
# 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:
Nun kann in die Datei <code>Butterfly.as</code> folgendener Code einfügt werden:
<source lang="actionscript">
<source lang="actionscript3">
package hsa.tutorial.butterfly
package hsa.tutorial.butterfly.view
{
{
   import flash.display.MovieClip;
   import flash.display.MovieClip;
Zeile 87: Zeile 109:
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////


     public static const OPENED:     String = "opened";
     public static const OPEN:       String = "open";
     public static const CLOSED:     String = "closed";
     public static const CLOSE:     String = "close";
     public static const FLUTTERING: String = "fluttering";
     public static const FLUTTERING: String = "fluttering";
    private static const c_label_prefix: String = "lb_";


     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
Zeile 110: Zeile 134:
       if (p_state != v_state)
       if (p_state != v_state)
       {
       {
         this.gotoAndPlay("lb_"+ p_state);
         this.gotoAndPlay(c_label_prefix + p_state);
         v_state = p_state;
         v_state = p_state;
       };
       };
Zeile 119: Zeile 143:
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
      
      
     public function Butterfly(p_state: String = OPENED)
     public function Butterfly(p_state: String = OPEN)
     {
     {
       this.state = p_state;
       this.state = p_state;
Zeile 134: Zeile 158:
Man beachte, dass im Gegensatz
Man beachte, dass im Gegensatz
zu den vorangegangen Tutorien die Labelbezeichnungen nicht
zu den vorangegangen Tutorien die Labelbezeichnungen nicht
mehr <code>lbOpened</code>, <code>lbClosed</code> und <code>lbFluttering</code>
mehr <code>lbOpen</code>, <code>lbClose</code> und <code>lbFluttering</code>
lauten, sondern <code>lb_opened</code>, <code>lb_closed</code> und <code>lb_fluttering</code>.
lauten, sondern <code>lb_open</code>, <code>lb_close</code> und <code>lb_fluttering</code>.


Gemäß den hier verwendeten [[Multimedia-Programmierung: Konventionen|Namenskonventionen]]
Gemäß den hier verwendeten [[Multimedia-Programmierung: Style Guide|Namenskonventionen]]
bedeutet dies, dass von außen nicht mehr direkt auf die Zeitleistenlabels zugegriffen werden soll.
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).
Sie werden als <code>private</code> angesehen (auch wenn dies in Flash nicht realisierbar ist).
Zeile 144: Zeile 168:


===Anmerkung 2===
===Anmerkung 2===
Für den Schmetterling wurde eine saubere Schnittstelle definiert (das [[Attribut]] <code>state</code>).
Für den Schmetterling wurde eine saubere Schnittstelle definiert (die [[Attribut]]e <code>state</code> und <code>stateAnimation</code>).
Nur über diese Schnittstelle soll von außen auf das Objekt zugegriffen werden.
Nur über diese Schnittstelle darf von außen auf ein Schmetterlingsobjekt zugegriffen werden.


Wenn man einen Schmetterling
Wenn man einen Schmetterling
auf die Bühne legt und ihm den „Instanznamen“ <code>sp_butterfly</code> gibt,
auf die Bühne legt und ihm den „Instanznamen“ <code>d_butterfly</code> gibt,
kann man mit folgenden Befehlen dessen Zustand verändern:
kann man mit folgenden Befehlen dessen Zustand verändern:


<source lang="actionscript">
<source lang="actionscript3">
import hsa.tutorial.butterfly.*;
import hsa.tutorial.butterfly.view.Butterfly;


sp_butterfly.state=Butterfly.OPENED;
d_butterfly.state=Butterfly.OPEN;
sp_butterfly.state=Butterfly.CLOSED;
d_butterfly.state=Butterfly.CLOSE;
sp_butterfly.state=Butterfly.FLUTTERING;
d_utterfly.state=Butterfly.FLUTTERING;
</source>
</source>


===Anmerkung 3===
Außerdem kann man den aktuellen Zustand des Schmetterling und auch seiner Flügel jederzeit lesen.
Bei jeder Zustandsänderung sollte das Schmetterlings-Objekt alle interessierten Objekte,  
 
d.h. alle [[Observer-Pattern|Observer]] (bzw in ActionScript: Listener)
Allerdings wurde das obige Klassendiagramm noch nicht vollständig implementiert.
Es fehlen noch zwei „Event Dispatcher“. Wie diese realisiert werden, wird im Folgenden gezeigt.
 
===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)
mit Hilfe eines [[Signal]]s (in ActionScript [[Event]] genannt)
über die erfolgte Zustandsänderung informieren. Dies wäre  
über die erfolgte Zustandsänderung informieren. Dies wäre  
mit Hilfe des Befehls
mit Hilfe des Befehls
<source lang="actionscript">
<source lang="actionscript3">
this.dispatchEvent(new Event(Event.CHANGE));
this.dispatchEvent(new Event(Event.CHANGE));
</source>
</source>
Zeile 171: Zeile 200:


Allerdings ist es sauberer, eine eigene Klasse <code>ButterflyEvent</code>
Allerdings ist es sauberer, eine eigene Klasse <code>ButterflyEvent</code>
als Subklasse von <code>Event</code> zu definieren und dort spezielle <code>BuuterflyEvent</code>-Konstanten zu definieren.
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</code>
Die zugehörige Datei <code>ButterflyEvent.as</code> muss, wenn als Package-Name ebenfalls <code>hsa.tutorial.butterfly.event</code>
angegeben wird, im selben Ordner wie die anderen <code>as</code>-Dateien abgespeichert werden: <code>hsa/tutorial/butterfly</code>.
angegeben wird, im Unterordner <code>hsa/tutorial/butterfly/event</code> gespeichert werden.
Der Inhalt dieser Datei könnte folgendermaßen aussehen:  
Der Inhalt dieser Datei könnte folgendermaßen aussehen:  


<source lang="actionscript">
<source lang="actionscript3">
package hsa.tutorial.butterfly
package hsa.tutorial.butterfly.event
{
{
   import flash.events.Event;
   import flash.events.Event;
Zeile 188: Zeile 217:
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
      
      
     public static const CHANGE_STATE:          String = "change state";
     public static const CHANGE_BUTTERFLY:          String
     public static const CHANGE_STATE_ANIMATION: String = "change state animation";
                          = "change butterfly";
     public static const CHANGE_BUTTERFLY_ANIMATION: String
                          = "change butterfly animation";
      
      
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
Zeile 195: Zeile 226:
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
      
      
     public function ButterflyEvent(type: String = CHANGE_STATE)
     public function ButterflyEvent(type: String = CHANGE_BUTTERFLY)
     {
     {
       super(type);
       super(type);
Zeile 210: Zeile 241:
in der Datei <code>Butterfly.as</code> erweitern:
in der Datei <code>Butterfly.as</code> erweitern:


<source lang="actionscript">
<source lang="actionscript3">
     public function set state(p_state: String): void
     public function set state(p_state: String): void
     {
     {
       if (p_state != v_state)
       if (p_state != v_state)
       {
       {
         this.gotoAndPlay("lb_"+ p_state);
         this.gotoAndPlay(c_label_prefix + p_state);
         v_state = p_state;
         v_state = p_state;
         this.dispatchEvent(new ButterflyEvent());
         this.dispatchEvent(new ButterflyEvent());
Zeile 222: Zeile 253:
</source>
</source>


Dies wird z.B. im nachfolgenden Beispiel ausgenutzt,
Damit jede Änderung der Flügelstellung ebenfalls mit Hilfe eines Signals (der Art <code>CHANGE_STATE_ANIMATION</code> ) angezeigt wird, ist
um den aktuellen Zustand in einem Textfeld ausgeben zu können. Hierzu wurde in der
ein wenig mehr Aufwand notwendig. In der Zeitleiste des Symbols <code>Butterfly</code> müssen geeignete Methodenaufrufe eingefügt werden:
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">
# In der Zeitleiste des Symbols <code>Butterfly</code>, Ebene <code>scripts</code> folgenden Code eintragen (Schlüsselbilder nicht vergessen!):
import hsa.tutorial.butterfly.ButterflyEvent;
#; 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>


// sp_butterfly ist der Schmettrling auf der Bühne.
Nun müssen noch die Methode <code>m_new_wings_state</code> sowie
sp_butterfly.addEventListener(ButterflyEvent.CHANGE_STATE, o_change);
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.


function o_change(p_event: ButterflyEvent): void
<source lang="actionscript3">
{
    private var v_state_animation: String;
  // sp_label_state ist ein Textfeld auf der Bühne.
  sp_label_state.text = sp_butterfly.state;
}
</source>


<swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/Butterfly07ButterflyRoughFlash11.swf</swf>
    public function get stateAnimation(): String
    {
      return v_state_animation;
    }


==Das Schmetterlingssymbol als Spielfigur mit weichen Übergängen==
    // Should be private, but cannot as it is accessed from within the timeline!
 
    public function m_new_wings_state(p_state: String): void
Einen Nachteil hat die vorangegangene Implementierung noch. Ein Übergang
    {
von einem Zustand zu einem anderen erfolgt im Allgemeinen ruckartig.
      if (p_state != v_state_animation)
Wenn man z.B. von CLOSED ind FLUTTERING wechselt, werden die Flügel
      {
schlagartig geöffnet.
        v_state_animation = p_state;       
 
        this.dispatchEvent
Sowohl beim vorangegangenen Beispielfilm, als auch bei nachfolgenden Beispielsfilm wird zunächst alle 30 Frames und danach kurzeitig viel häufiger der Zustand des Schmetterlings geändert.
          (new ButterflyEvent(ButterflyEvent.CHANGE_BUTTERFLY_ANIMATION));
Man beachte den Unterschied.
      };
 
     }
<swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/Butterfly07ButterflySmoothFlash11.swf</swf>
</source>
 
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 <code>Butterfly</code> notwendig:
 
# In der Zeitleiste, Ebene <code>labels</code>: Labelnamen von Frame 40 in <code>lb_fluttering_opened</code> abändern.
# In der Zeitleiste, Ebene <code>labels</code>: Folgenden Labelnamen in Frame 50 einfügen <code>lb_fluttering_closed</code>.
# In der Zeitleiste, Ebene <code>scripts</code> folgenden zusätzlichen Code eintragen (Schlüsselbilder nicht vergessen!):
#; Frame 40:
#: <code>m_new_wings_state(OPENED);</code>
#; Frame 50:
#: <code>m_new_wings_state(CLOSED);</code>
 
Die Implementierung der Setter-Methode <code>set state</code> 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:
 
{| class="toccolours" border="1" cellpadding="4" style="margin: 0 0 1em 1em; font-size: 95%; border-collapse: collapse;"
|-
! Aktueller Zustand     !! Neuer Zustand                !! Aktion
|-
| <code>OPENED</code>    || <code>FLUTTERING</code>      || <code>gotoAndPlay("lb_fluttering_opened")</code> ⇒ der neue Status wird sofort erreicht
|-
| <code>CLOSED</code>    || <code>FLUTTERING</code>      || <code>gotoAndPlay("lb_fluttering_closed")</code> ⇒ der neue Status wird sofort erreicht
|}
 
In den übrigen vier Fällen muss der Statuswechsel verzögert werden:
 
{| class="toccolours" border="1" cellpadding="4" style="margin: 0 0 1em 1em; font-size: 95%; border-collapse: collapse;"
|-
! Aktueller Zustand      !! Neuer Zustand                !! Aktion
|-
| <code>OPENED</code>     || <code>CLOSED</code>          || <code>gotoAndPlay("lb_fluttering_opened")</code><br /> Aufruf von <code>m_new_wings_state(CLOSED)</code> ⇒ neuer Status ist erreicht
|-
| <code>CLOSED</code>    || <code>OPENED</code>          || <code>gotoAndPlay("lb_fluttering_closed")</code><br /> Aufruf von <code>m_new_wings_state(OPENED)</code> ⇒ neuer Status ist erreicht 
|-
| <code>FLUTTERING</code> || <code>CLOSED</code>          || Aufruf von <code>m_new_wings_state(CLOSED)</code> ⇒ neuer Status ist erreicht
|-
| <code>FLUTTERING</code> || <code>OPENED</code>          || Aufruf von <code>m_new_wings_state(OPENED)</code> ⇒ 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:
Insgesamt sieht der Inhalt der Datei <code>Butterfly.as</code> nun folgendermaßen aus:


{| class="toccolours" border="1" cellpadding="4" style="margin: 0 0 1em 1em; font-size: 95%; border-collapse: collapse;"
<source lang="actionscript3">
|-
package hsa.tutorial.butterfly.view
! Aktueller Zustand      !! Neuer Zustand                !! Aktion
|-
| irgendein Zustand      || <code>FLUTTERING</code>      || Der Schmetterling hat den neuen Zustand bereits erreicht, da er bereits flattert.<br/> Die verzögerte Zustandsänderung muss(!) unterbrochen werden.
|-
| <code>OPENED</code> oder<br/><code>CLOSED</code>  || <code>CLOSED</code>  || Aufruf von <code>m_new_wings_state(CLOSED)</code> ⇒ neuer Status ist erreicht
|-
| <code>OPENED</code> oder<br/><code>CLOSED</code>  || <code>OPENED</code>  || Aufruf von <code>m_new_wings_state(OPENED)</code> ⇒ neuer Status ist erreicht
|}
 
Wenn man die obigen Tabellen in Code umsetzt, ändert sich der Inhalt der
Datei <code>Butterfly.as</code> wie folgt:
 
<source lang="actionscript">
/////////////////////////////////////////////////////////////////////////////////
// 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
{
{
   import flash.display.MovieClip;
   import flash.display.MovieClip;
  import hsa.tutorial.butterfly.event.ButterflyEvent;
    
    
   public class ButterflySmooth extends MovieClip
   public class Butterfly extends MovieClip
   {
   {
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
     // Constants
     // Constants
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
   
    public static const OPEN:      String = "open";
    public static const CLOSE:      String = "close";
    public static const FLUTTERING: String = "fluttering";


    public static const OPENED:    String = "opened";
     private static const c_label_prefix: String = "lb_";
    public static const CLOSED:    String = "closed";
    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
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
    // Instance variables of the attributes
    private var v_state:          String;
      
      
     // Auxiliary variable (for being able to perform delayed state changes)
     private var v_state:          String;
     private var v_next_state: String = null;
     private var v_state_animation: String;


     /////////////////////////////////////////////////////////////////////////////
     ////////////////////////////////////////////////////////////////////////////
     // Private methods
     // Methods to be accessed from within the timeline!
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
      
      
     private function m_set_state(p_state: String): void
     // Should be private, but cannot as it is accessed from within the timeline!
    public function m_new_wings_state(p_state: String): void
     {
     {
       if (v_state != p_state)
       if (p_state != v_state_animation)
       {
       {
         v_state = p_state;
         v_state_animation = p_state;    
         this.dispatchEvent(new ButterflyEvent());
         this.dispatchEvent
       }
          (new ButterflyEvent(ButterflyEvent.CHANGE_BUTTERFLY_ANIMATION));
       };
     }
     }
   
 
     ////////////////////////////////////////////////////////////////////////////
     ////////////////////////////////////////////////////////////////////////////
     // Attributes
     // Attributes
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
 
      
     /**
    * The current state of the butterfly:
    *  <code>OPENED</code>, <code>CLOSED</code> or <code>BUTTERFLY</code>.
    */
     public function get state(): String
     public function get state(): String
     {
     {
Zeile 383: Zeile 339:
      
      
     public function set state(p_state: String): void
     public function set state(p_state: String): void
     {    
     {
       if (p_state != OPENED && p_state != CLOSED && p_state != FLUTTERING)
       trace(v_state + " ---> " + p_state);
        throw (new Error("ButterflySmooth: State '" + p_state + "' is unknown!"));


       if (v_next_state == null) // Currently no delayed state change is performed!
       if (p_state != v_state)
      {
        if (p_state == FLUTTERING)
        {
          this.gotoAndPlay(  c_label_prefix_fluttering
                          + ((v_state == null) ? OPENED : v_state)
                          );
          m_set_state(p_state); // Do the state change now!
        }
        else // p_state == OPENED || CLOSED
        {
          if (v_state != FLUTTERING)
            this.gotoAndPlay(  c_label_prefix_fluttering
                            + ((v_state == null) ? OPENED : 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!
       {
       {
         if (p_state == FLUTTERING)
         this.gotoAndPlay(c_label_prefix + p_state);
         {
         v_state = p_state;
          v_next_state = null; // Stop the delayed state change.
        this.dispatchEvent(new ButterflyEvent());
          m_set_state(p_state); // Do the state change now!
      };
        }
        else
          v_next_state = p_state; // Update the delayed state change;
      }
     }
     }
      
      
    /////////////////////////////////////////////////////////////////////////////
     public function get stateAnimation(): String
    // 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 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
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
      
      
     public function ButterflySmooth(p_state: String = OPENED)
     public function Butterfly(p_state: String = OPEN)
     {
     {
       this.state = p_state;
       this.state = p_state;
     }
     }
 
   
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
     // End of class
     // End of class
Zeile 451: Zeile 371:
</source>
</source>


===Anmerkung 1===
Damit ist es z.B. möglich, einen Flügelschlag-Sound mit dem Movie zu synchronisieren.
Man beachte, dass hier nur eine verbesserte, wenn auch viel aufwändigere Implementierung des Symbols <code>Butterfly</code>
Im folgenden Beispiel wird der aktuelle Zustand des Flügels in einem zweiten Textfeld ausgegeben.
angegeben wurde. An der zuvor definierten Schnittstellte ändert sich nichts.
Ein drittes Textfeld simuliert den Flip-Flap-Sound (da mir derzeit leider kein echter Flügelschlag-Sound vorliegt).
Man kann sich allerdings nicht mehr darauf verlassen, dass die Zustandswechsel
sofort in dem Moment angezeigt wird, in dem die Setter-Methode <code>set state</code>
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,
=Testen der Klassen=
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:  
<swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07a_character/Butterfly07aFlash11.swf</swf>
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:
Um diese Verhalten zu erreichen, wird auf die Bühne ein Schmetterlingsobjekt gelegt und der Name <code>d_butterfly</code> zugeordnet.
Anschließend wird in der Hauptzeitleiste in regelmäßigen Abständen
einer der folgenden drei Befehle eingefügt:


# 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>.
* <code>d_butterfly.state=Butterfly.OPEN;</code>
# In der Zeitleiste, Ebene <code>labels</code>: Die Label <code>lb_butterfly_opened</code> und <code>lb_butterfly_closed</code> in <code>lb_opened</code> und <code>lb_closed</code> umbenennen.
* <code>d_butterfly.state=Butterfly.CLOSE;</code>
* <code>d_butterfly.state=Butterfly.FLUTTERING;</code>


Den Code in der Datei <code>Butterfly.as</code> durch folgenden Code ersetzen:
Zuletzt wird der Bühne
die Klasse <code>hsa.tutorial.butterfly.Main</code> zugeordnet
(wobei sich die zugehörige Datei <code>Main.as</code> im Unterordner <code>hsa/tutorial/butterfly</code> befindet):


<source lang="actionscript">
<source lang="actionscript3">
package hsa.tutorial.butterfly
package hsa.tutorial.butterfly
{
{
   import flash.display.MovieClip;
   import flash.display.MovieClip;
  import hsa.tutorial.butterfly.event.ButterflyEvent;
  import hsa.tutorial.butterfly.view.Butterfly;
  import fl.transitions.Tween;
  import fl.transitions.easing.None;
    
    
   public class Butterfly extends MovieClip
   public class Main extends MovieClip
   {
   {
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
     // Constants
     // Instance Variables
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
    public static const OPENED:    String = "opened";
    public static const CLOSED:    String = "closed";
    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_tween: Tween;
     private var v_stop_state: String = null;
   
 
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
     // Private methods
     // MovieClips on the stage
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
      
      
     private function m_set_state(p_state: String): void
     public var d_butterfly: Butterfly;
    {
      if (v_state != p_state)
      {
        v_state = p_state;
        this.dispatchEvent(new ButterflyEvent());
      }
    }
      
      
    ////////////////////////////////////////////////////////////////////////////
    // Attributes
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
 
     // Constructor
    public function get state(): String
     /////////////////////////////////////////////////////////////////////////////
     {
      return v_state;
     }
      
      
     public function set state(p_state: String): void
     public function Main()
     {
     {
       if (p_state == FLUTTERING)
       d_butterfly
      {
         .addEventListener(ButterflyEvent.CHANGE_BUTTERFLY,          o_change);
        v_stop_state = null; // Don't stop fluttering!
       d_butterfly
         this.play();        // Start the movie (if it currently is stoped).
         .addEventListener(ButterflyEvent.CHANGE_BUTTERFLY_ANIMATION, o_wings);
        m_set_state(p_state); // Do the state change now!
       }
      else if (p_state == OPENED || p_state == CLOSED)
      {
         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)
     // Observer methods
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
      
      
     public function m_new_wings_state(p_state: String): void
     private function o_change(p_event: ButterflyEvent): void
     {
     {
       // If the wings are to be stoped, stop them.
       d_label_state.text = d_butterfly.state;
      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 = OPENED)
     private function o_wings(p_event: ButterflyEvent): void
     {
     {
      this.state = p_state;
      d_label_wings.text = d_butterfly.stateAnimation;
    }
     
      d_label_flap.text = (d_butterfly.stateAnimation == Butterfly.OPEN)
        ? "flip"
        : "flap";


      if (v_tween != null && v_tween.isPlaying)
        v_tween.stop();
     
      v_tween =
        new Tween
        ( d_label_flap,  // the 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)
        );
     
      v_tween.start();
    }   
     /////////////////////////////////////////////////////////////////////////////
     /////////////////////////////////////////////////////////////////////////////
     // End of class
     // End of class
Zeile 578: Zeile 465:
</source>
</source>


===Anmerkung 1===
Beachten Sie, dass der Code viel kürzer, einfacher und verständlicher ist, als im vorangegangen Beispiel.
==Ein zweiter Event Dispatcher==
Gemäß dem oben angeführten Datenmodell muss in die Klasse <code>Butterfly</code> noch ein zweiter
Event-Dispatcher eingefügt werden. Bei jedem Öffnen oder Schließen der Flügel
soll zusätzlich ein Event der Art <code>CHANGE_STATE_ANIMATION</code> gemeldet werden.
Auf den aktuellen Zustand der Flügel kann man von außen mit Hilfe
der ebenfalls neu zu definierten [[Getter-Methode]] <code>get stateAnimation</code>
zugreifen.
Um dies zu erreichen, kann man die Methoden <code>m_new_wings_state</code>
die in der zweiten und dritten der obigen <code>Butterfly</code>-Versionen
von der Timeline aus aufgefrufen werden, erweitern.
Zunächst muss ein zusätzliches [[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_timeline;
    }
</source>
Nun wird die Methode <code>m_new_wings_state</code> erweitert.
Zunächst ändert sie den Wert des neuen Attributs
<code>stateAnimation</code> und informiert per <code>dispatchEvent</code>
die an diesem Ereignis interessierten Observer. Anschließend führt sie,
wie zuvor schon, eine verzögerte Zustandsänderung durch, sofern diese
zuvor initiiert wurde.
<source lang="actionscript">
    public function m_new_wings_state(p_state: String): void
    {
      // The state of the wings has changed. Inform the observers!
      v_state_animation = p_state;
      this.dispatchEvent
        (new ButterflyEvent(ButterflyEvent.CHANGE_STATE_ANIMATION));
      // 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!
      };
    }
</source>
Damit ist es z.B. möglich, einen Flügelschlag-Sound mit dem Movie zu synchronisieren.
Im folgenden 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 Flügelschlag-Sound vorliegt).
<swf width="183" height="133">http://glossar.hs-augsburg.de/beispiel/tutorium/flash_cs5/butterfly/butterfly_07_character/Butterfly07ButterflySimplifiedFlash11.swf</swf>
Der entsprechende Code im ersten Frame der Haupt-Zeitleiste des Test-Movies
lautet:
<source lang="actionscript">
import hsa.tutorial.butterfly.Butterfly;
import hsa.tutorial.butterfly.ButterflyEvent;
import fl.transitions.Tween;
import fl.transitions.easing.*;
sp_butterfly.addEventListener(ButterflyEvent.CHANGE_STATE,          o_change);
sp_butterfly.addEventListener(ButterflyEvent.CHANGE_STATE_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;
 
  if (sp_butterfly.stateAnimation == Butterfly.OPENED)
    sp_label_flap. text = "flip";
  else
    sp_label_flap. text = "flap";
  new Tween(sp_label_flap, // object to be animated
            "alpha",      // animation attribute: "x", "y", "alpha", "rotation", ...
            None.easeIn,  // animation type
            1.0, 0.0,      // start and end value (of alpha)
            15,            // Dauer der Animation ...
            false          // duration in frames (not in seconds)
          );
}
</source>
==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_07a_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_07a_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_07a_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_07a_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_07a_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_07_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_07a_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_07a_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 25. Oktober 2014, 14:46 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

Weiterentwicklung der sechsten Version des Schmetterling-Movies

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

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


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 Butterfly und ButterflyMovie in „Spielfiguren“ (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 Butterfly in eine Spielfigur (im zuvor beschrieben Sinn) umgewandelt, um das Grundprinzip klar zu machen. In zwei weiteren Schritten (Teil 7b, Teil 7c) wird die Implementierung verbessert, so dass die Übergänge von einem Zustand in den anderen „weich“ erfolgen. Auf die zur Verfügung gestellte Schnittstelle hat dies keine Auswirkungen.

Zu guter Letzt (Teil 8) wird das Symbol ButterflyMovie ebenfalls in eine „Spielfigur“ umgewandelt.

Das Datenmodell

Bevor mit der Programmierung einer Anwendung begonnen wird, sollte zunächst ein Datenmodell (Klassendiagramm, Zustandsdiagramm etc.) erstellt werden. Folgendes Klassendiagramm wird in diesem Teil des Tutorium realisiert:

AS3-Tutorium Butterfly Flash 07 Class Diagram.jpg

Ordner mit StarUML-Diagramm und von StarUML erzeugtem AS3-Code-Rahmen (SVN-Repository)

Es gibt eine Klasse zur Visualisierung einer Spielfigur (eine so genannte View-Klasse):

  • Butterfly, eine Spielfigur, die sich nicht vom Platz bewegt, aber verschiedene Zustände annehmen kann.

Diese Klasse ist, da sie Symbolen zugeordnet wird, Subklassen der ActionScript-Klasse MovieClip.

Ein Schmetterling kann drei Zustände annehmen:

  • Flügel offen (OPEN)
  • Flügel geschlossen (CLOSE)
  • flatternd (FLUTTERING)

Dieser Zustand kann über das Attribut state gelesen und modifiziert werden. Mit Hilfe des Attributs stateAnimation 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 Signalen (Events) interessierte Beobachter (Observers, Event Listeners) sowohl dann, wenn sich der Zustand (state) ändert, als auch dann, wenn sich der Zustand der Animation (stateAnimation), d.h. der Flügel ändert. Dazu wird die Signal-Klasse ButterflyEvent (eine Subklasse der ActionScript-Klasse Event) definiert, die zwei Konstanten zur Beschreibung dieser Ereignistypen bereithält.

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. lb_x, wobei x durch den aktuellen Zustand ersetzt wird).

Das letzte Frame eines jeden Abschnittes wird mit einem ActionScript-Befehl versehen:

  • stop();, falls die dem Zustand zugeordnete Animation nur einmal abgespielt werden soll
  • gotoAndPlay(lb_x);, falls die dem Zustand zugeordnete Animation dauerhaft abgespielt werden soll

Zunächst wird das Schmetterlingssymbol auf diese Weise umgestaltet.

  1. In der Bibliothek: Das Symbol Butterfly mit einem Doppelklick öffnen.
  2. In der Zeitleiste: Zwei neue Ebenen (ganz oben) einfügen:
    • scripts
    • labels
  3. 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.
  4. In der Zeitleiste, Ebene body: Klick auf das Schlüsselbild bei Frame 40 → Alt-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.
  5. In der Zeitleiste, Ebene body: Das Schlüssenbild bei Frame 40 entfernen: Klick auf das Schlüsselbild → Rechtsklick auf das Schlüsselbild → Schlüsselbild löschen.
  6. In der Zeitleiste, Ebene wings: Klick auf Frame 20 → Rechtsklick auf Frame 20 → Schlüsselbild einfügen.
  7. In der Zeitleiste, Ebene wings: Strg/Ctrl-Taste drücken und festhalten → Klick auf das Schlüsselbild bei Frame 40 → Rechtsklick auf das Schlüsselbild → Bilder kopieren.
  8. In der Zeitleiste, Ebene wings: Klick auf Frame 1 → Rechtsklick auf Frame 1 → Bilder einfügen (nicht Bild einfügen!!).
  9. In der Zeitleiste, Ebene wings: Strg/Ctrl-Taste drücken und festhalten → Klick auf das Schlüsselbild bei Frame 50 → Rechtsklick auf das Schlüsselbild → Bilder kopieren.
  10. In der Zeitleiste, Ebene wings: Klick auf Frame 20 → Rechtsklick auf Frame 20 → Bilder einfügen (nicht Bild einfügen!!).
  11. In der Zeitleiste, Ebene labels: Klick auf Frame 1 → Eigenschaften-Fenster öffnen → Name: lb_open
  12. In der Zeitleiste, Ebene labels: Klick auf Frame 20 → Rechtsklick auf Frame 20 → Schlüsselbild einfügen → Eigenschaften-Fenster, Name: lb_close
  13. In der Zeitleiste, Ebene labels: Klick auf Frame 40 → Eigenschaften-Fenster, Name: lb_fluttering
  14. In der Zeitleiste, Ebene scripts folgenden Code eintragen (Schlüsselbilder nicht vergessen!):
    Frame 19
    stop();
    Frame 39
    stop();
    Frame 59
    gotoAndPlay("lb_fluttering");

Als nächstes wird der Code zur Steuerung des Zustandswechsels dem Schmetterlings-Symbol zugeordnet:

  1. Im Menü: DateiNeuActionScript 3.0-Klasse (CS5) bzw. ActionScript-Datei (CS4 und früher) → OK
    Nur in CS5: -> Klassenname: ButterflyOK
  2. Im Menü: DateiDatei speichern unter → Im Ordner der fla-Datei folgende Unterordner anlegen: hsa/tutorial/butterfly/view → Datei unter dem Namen Butterfly.as im zuvor angelegten Unterordner speichern.
  3. In der Bilbliothek: Rechtsklick auf das Symbol ButterflyEigenschaften → falls notwendig: Klick auf Dreieck hinter Erweitert → Häckchen vor Export für ActionScript → Klasse: hsa.tutorial.butterfly.view.ButterflyBlue (oder eine andere Farbe), Basisklasse hsa.tutorial.butterfly.view.ButterflyOKOK

Nun kann in die Datei Butterfly.as folgendener Code einfügt werden:

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";

    private static const c_label_prefix: String = "lb_";

    /////////////////////////////////////////////////////////////////////////////
    // 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(c_label_prefix + p_state);
        v_state = p_state;
      };
    }

    /////////////////////////////////////////////////////////////////////////////
    // Constructor
    /////////////////////////////////////////////////////////////////////////////
    
    public function Butterfly(p_state: String = OPEN)
    {
      this.state = p_state;
    }
    
    /////////////////////////////////////////////////////////////////////////////
    // End of class
    /////////////////////////////////////////////////////////////////////////////
  }
}

Anmerkung 1

Man beachte, dass im Gegensatz zu den vorangegangen Tutorien die Labelbezeichnungen nicht mehr lbOpen, lbClose und lbFluttering lauten, sondern lb_open, lb_close und lb_fluttering.

Gemäß den hier verwendeten Namenskonventionen bedeutet dies, dass von außen nicht mehr direkt auf die Zeitleistenlabels zugegriffen werden soll. Sie werden als private angesehen (auch wenn dies in Flash nicht realisierbar ist). Das heißt, auf die Labels wird nur noch innerhalb der Klasse Butterlfy zugegrifen (konkret: in der Setter-Methode set state).

Anmerkung 2

Für den Schmetterling wurde eine saubere Schnittstelle definiert (die Attribute state und stateAnimation). Nur über diese Schnittstelle darf von außen auf ein Schmetterlingsobjekt zugegriffen werden.

Wenn man einen Schmetterling auf die Bühne legt und ihm den „Instanznamen“ d_butterfly gibt, kann man mit folgenden Befehlen dessen Zustand verändern:

import hsa.tutorial.butterfly.view.Butterfly;

d_butterfly.state=Butterfly.OPEN;
d_butterfly.state=Butterfly.CLOSE;
d_utterfly.state=Butterfly.FLUTTERING;

Außerdem kann man den aktuellen Zustand des Schmetterling und auch seiner Flügel jederzeit lesen.

Allerdings wurde das obige Klassendiagramm noch nicht vollständig implementiert. Es fehlen noch zwei „Event Dispatcher“. Wie diese realisiert werden, wird im Folgenden gezeigt.

Signale

Bei jeder Zustandsänderung soll das Schmetterlings-Objekt laut Klassendiagramm alle interessierten Objekte, d.h. alle Observer (bzw. in ActionScript: Event Listener) mit Hilfe eines Signals (in ActionScript Event genannt) über die erfolgte Zustandsänderung informieren. Dies wäre mit Hilfe des Befehls

this.dispatchEvent(new Event(Event.CHANGE));

problemlos möglich.

Allerdings ist es sauberer, eine eigene Klasse ButterflyEvent als Subklasse von Event zu definieren und dort spezielle ButterflyEvent-Konstanten einzuführen.

Die zugehörige Datei ButterflyEvent.as muss, wenn als Package-Name ebenfalls hsa.tutorial.butterfly.event angegeben wird, im Unterordner hsa/tutorial/butterfly/event gespeichert werden. Der Inhalt dieser Datei könnte folgendermaßen aussehen:

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
    /////////////////////////////////////////////////////////////////////////////
  }
}

Sobald man diese Datei erstellt hat, kann man die Setter-Methode set state in der Datei Butterfly.as erweitern:

    public function set state(p_state: String): void
    {
      if (p_state != v_state)
      {
        this.gotoAndPlay(c_label_prefix + p_state);
        v_state = p_state;
        this.dispatchEvent(new ButterflyEvent());
      };
    }

Damit jede Änderung der Flügelstellung ebenfalls mit Hilfe eines Signals (der Art CHANGE_STATE_ANIMATION ) angezeigt wird, ist ein wenig mehr Aufwand notwendig. In der Zeitleiste des Symbols Butterfly müssen geeignete Methodenaufrufe eingefügt werden:

  1. In der Zeitleiste des Symbols Butterfly, Ebene scripts folgenden Code eintragen (Schlüsselbilder nicht vergessen!):
    Frame 1
    m_new_wings_state(OPEN);
    Frame 20
    m_new_wings_state(CLOSE);
    Frame 40
    m_new_wings_state(OPEN);
    Frame 50
    m_new_wings_state(CLOSE);

Nun müssen noch die Methode m_new_wings_state sowie das Attribut stateAnimation in die Klasse Butterfly eingefügt werden. Man beachte, dass es keine Setter-Methode gibt. Das Attribut kann von außen nur gelesen werden.

    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
    {
      if (p_state != v_state_animation)
      {
        v_state_animation = p_state;      
        this.dispatchEvent
          (new ButterflyEvent(ButterflyEvent.CHANGE_BUTTERFLY_ANIMATION));
      };
    }

Insgesamt sieht der Inhalt der Datei Butterfly.as nun folgendermaßen aus:

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_";

    /////////////////////////////////////////////////////////////////////////////
    // 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
    {
      trace(v_state + " ---> " + p_state);

      if (p_state != v_state)
      {
        this.gotoAndPlay(c_label_prefix + 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
    /////////////////////////////////////////////////////////////////////////////
  }
}

Damit ist es z.B. möglich, einen Flügelschlag-Sound mit dem Movie zu synchronisieren. Im folgenden 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).

Testen der Klassen

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

Um diese Verhalten zu erreichen, wird auf die Bühne ein Schmetterlingsobjekt gelegt und der Name d_butterfly zugeordnet. Anschließend wird in der Hauptzeitleiste in regelmäßigen Abständen einer der folgenden drei Befehle eingefügt:

  • d_butterfly.state=Butterfly.OPEN;
  • d_butterfly.state=Butterfly.CLOSE;
  • d_butterfly.state=Butterfly.FLUTTERING;

Zuletzt wird der Bühne die Klasse hsa.tutorial.butterfly.Main zugeordnet (wobei sich die zugehörige Datei Main.as im Unterordner hsa/tutorial/butterfly befindet):

package hsa.tutorial.butterfly
{
  import flash.display.MovieClip;
  import hsa.tutorial.butterfly.event.ButterflyEvent;
  import hsa.tutorial.butterfly.view.Butterfly;
  import fl.transitions.Tween;
  import fl.transitions.easing.None;
  
  public class Main extends MovieClip
  {
    /////////////////////////////////////////////////////////////////////////////
    // Instance Variables
    /////////////////////////////////////////////////////////////////////////////
    
    private var v_tween: Tween;
    
    /////////////////////////////////////////////////////////////////////////////
    // MovieClips on the stage
    /////////////////////////////////////////////////////////////////////////////
    
    public var d_butterfly: Butterfly;
    
    /////////////////////////////////////////////////////////////////////////////
    // Constructor
    /////////////////////////////////////////////////////////////////////////////
    
    public function Main()
    {
      d_butterfly
        .addEventListener(ButterflyEvent.CHANGE_BUTTERFLY,           o_change);
      d_butterfly
        .addEventListener(ButterflyEvent.CHANGE_BUTTERFLY_ANIMATION, o_wings);
    }
    
    /////////////////////////////////////////////////////////////////////////////
    // Observer methods
    /////////////////////////////////////////////////////////////////////////////
    
    private function o_change(p_event: ButterflyEvent): void
    {
      d_label_state.text = d_butterfly.state;
    }
    
    private function o_wings(p_event: ButterflyEvent): void
    {
      d_label_wings.text = d_butterfly.stateAnimation;
      
      d_label_flap.text = (d_butterfly.stateAnimation == Butterfly.OPEN)
        ? "flip"
        : "flap";

      if (v_tween != null && v_tween.isPlaying)
        v_tween.stop();
      
      v_tween = 
        new Tween
        ( d_label_flap,  // the 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)
        );
      
      v_tween.start();
    }    
    /////////////////////////////////////////////////////////////////////////////
    // End of class
    /////////////////////////////////////////////////////////////////////////////
  }
}

Quellen

SVN-Repository-Verweise


Dieser Artikel ist GlossarWiki-konform.