Anzeige
Tutorialbeschreibung

Menü mit trigonometrischer Funktion

Menü mit trigonometrischer Funktion

Dieser Workshop ist in Antwort auf eine Frage im Forum entstanden, bei dem es um einen Menüeffekt mit Hilfe einer einfachen trigonometrischen Funktion geht.


Version: ab Flash MX
Niveau: Einsteiger
Vorkenntnisse: Flash (Symbole, Zeitleisten), Grundlagen Actionscript (Arrays, for- und for in-Schleifen, Funktionen mit Parameter)
Lernziele: eine trigonometrische Funktion einsetzen lernen, das Prinzip der dynamischen Animation (hier: Abbremsen) verstehen lernen

Im Forum http://www.psd-tutorials.de/postt4803.html wurde die Frage gestellt, wie das Menü auf http://www.michaeltolcher.com/host.html funktioniere. Das dortige Menü besteht in der linken oberen Ecke aus einem windroseähnlichen Kreis, der mit einer Spitze auf den gerade aktiven Menüpunkt zeigt. Die Links sind in einem Viertelkreis rechts neben der Windrose angeordnet. Bei Rollover über einen Link dreht sich die Windrose zum aktivierten Element hin. Da mir der Sourcecode natürlich nicht zur Verfügung steht, kann hier nur eine mögliche Lösung vorgestellt werden. Sie beruht auf einer Umkehrfunktion der trigonometrischen Funktionen. Um Mißverständnisse zu vermeiden: hier wird keine umfassende Einführung in mathematische und physikalische Grundlagen und deren Umsetzung in Actionscript gegeben, sondern nur das konkrete Menüproblem besprochen. Ein ausführlicheres Tutorial zu Trigonometrie & Co. wird es zu einem späteren Zeitpunkt geben (mit Schwerpunkt auf Spieleprogrammierung).

Da es hier nur um das Prinzip geht, wollen wir uns mit einem minimalistischen Design begnügen, bestehend aus einem Zeiger sowie 5 im Viertelkreis angeordneten Navigationspunkten.

  1. Erstellen Sie einen neuen Film (800 x 600, weiß, 18 Bps).
  2. Benennen Sie die erste Ebene „actions“.
  3. Fügen Sie eine weitere Ebene namens „nav“ hinzu.
  4. Erstellen Sie auf der Ebene „nav“ einen zeiger- oder pfeilähnlichen MovieClip (Breite 100, Höhe 17, Registrierungspunkt am linken Rand, Bibliotheksname „mc_recht“, Instanzname „recht“). Die korrekte Position des Registrierungspunktes ist wichtig, da er unseren Drehpunkt darstellt. Achten Sie zudem unbedingt darauf, daß der Zeiger nach rechts ausgerichtet ist:

Bilder

  1. Positionieren Sie den Zeiger auf x = 20 und y = 50.
  2. Erstellen Sie auf derselben Ebene einen MovieClip, den wir als Button verwenden wollen (Bibliotheksname „mc_b_1“, Instanzname kurz und knackig „b_1“). Dieser Button besteht aus:
  • einem statischenTextfeld, Verdana, 14, schwarz, nicht auswählbar, Textinhalt „Punkt eins“.
  • einem hinter dem Textfeld liegenden MovieClip (Bibliotheksname beliebig, Instanzname nicht notwendig). Wenn wir darauf verzichten, dann werden nur die Buchstaben als Hotspot wahrgenommen, und das Aktivieren des Buttons über den Schriftzug entwickelt sich zum nervenaufreibenden Geduldsspiel.
  • einem kreisförmigen MovieClip (beliebiger Bibliotheksname, Instanzname „dot“) mit zwei Frames. Im ersten Frame wird ein blauer Punkt, im zweiten Frame ein orangefarbener Punkt angezeigt. Bei Rollover und MouseDown soll jeweils der zweite Frame zu sehen sein, ansonsten immer der erste.

Er könnte z.B. so aussehen:

Bilder

Auf die konkrete Erstellung und Verwendung von Symbolen wird hier nicht weiter eingegangen, da sie als Grundlagen vorausgesetzt werden.

  1. Duplizieren Sie den Button viermal („mc_b_2“ bis „mc_b_5“ bzw „b_2“ bis „b_5“, Textinhalt „Punkt Zwei“ bis „Punkt Fünf“).
  2. Ordnen Sie die Buttons sowie den Pfeil wie folgt an:

Bilder

Die Positionen der Buttons lauten: „b_1“._x = 159, „b_1“._y = 50; „b_2“: 155,86; „b_3“: 140, 121; „b_4“: 112, 152; „b_5“: 68, 178.

Falls Sie lieber eine etwas exaktere kreisförmige Anordnung konstruieren wollen, können Sie als Hilfsmittel auf einer neu einzufügenden Ebene namens „temp“ einen Kreis zeichnen, dessen Mittelpunkt durch den Drehpunkt des Pfeiles geht. Auf seinem Rand werden dann bei aktiviertem Magneten die Buttons abgelegt. Vergessen Sie nicht, die Ebene „temp“ abschließend wieder zu löschen.

Bilder
 

Um allen Navigationspunkten bzw. Schaltflächen das gleiche Verhalten zuweisen zu können – sie sollen nämlich dafür sorgen, das der Pfeil auf sie zeigt -, erfassen wir zunächst alle Namen in einem Array.

  1. Weisen Sie dem ersten Frame in der Ebene „actions“ folgendes Skript zu:

aBtns = [];
for(i = 1; i <= 5; i++){
          aBtns.push(„b_“+i);
}
for (e in aBtns) {
          this[(aBtns[e])].dot.stop();
          this[(aBtns[e])].onRollOver = function() {
                    this.dot.gotoAndStop(2);
          }
}

Es wird ein leeres Array definiert, in das wir mit der for-Schleife die Instanznamen der Buttons, also b_1, b_2 etc. eintragen. Für den späteren Zugriff ist zu beachten, daß die Namen alle als String, also simpler Text, vorliegen (deswegen die „“ bei der push-Methode). Dieses Vorgehen ist erheblich einfacher, als wenn wir händisch die Namen „b_1“, „b_2“ usw. eingetragen hätten. Zudem läßt sich das Array damit schnell ergänzen, falls noch weitere Navigationspunkte hinzukommen sollen.

In der for-Schleife werden zunächst alle dot-Instanzen innerhalb der Buttons gestoppt, da sonst die Animation (Kreis blau, Kreis orange) endlos abspielt. Der etwas merkwürdige Klammerausdruck this[(aBtns[e])] ist notwendig, um aus den Strings im Array ein Objekt, hier: einen MovieClip, zu machen. Sie können die runden Klammern auch weglassen; sie wurden hier nur gesetzt, um die Reihenfolge, in der die Ausdrücke abzuarbeiten sind, hervorzuheben: erst wird der Inhalt der runden Klammer ermittelt, dann macht Flash daraus ein Objekt (oder präziser: einen Objektpfad).

Bevor wir uns mit der Umsetzung des Menüs weiter befassen, ein paar Worte zur zugrunde liegenden Trigonometrie. Mit Hilfe von trigonometrischen Funktionen lassen sich bei bekannten Größen eines Dreiecks (wie Seitenlängen und Winkelgrößen) andere Größen errechnen. Am einfachsten ist dies bei einem rechtwinkligen Dreieck, das in der folgenden Grafik in den sogenannten Einheitskreis eingefügt wurde.

Bilder

Unter dem Einheitskreis versteht man einen Kreis mit dem Radius 1 und einem rechtwinkligen Koordinatensystem, das durch seinen Mittelpunkt geht. Die konkrete Längeneinheit spielt keine Rolle, ob 1 Kilometer, 1mm oder auch 75 Pixel ist also unerheblich.

In unserem Beispiel sind sowohl die Position des Pfeils, dessen Ausgangswinkel sowie die Position des aktivierten Navigationspunktes bekannt. Übertragen auf ein Dreieck sind infolgedessen die Ankathete und die Gegenkathete prinzipiell bekannt, der Drehwinkel a dagegen soll ermittelt werden.

Ohne auf die Trigonometrie im Einzelnen eingehen zu wollen (das soll ja Teil eines größeren Workshops werden), ist hier nur noch wichtig, daß der Winkel über die Math.athan2-Methode berechnet wird. Das ist eine bereits in Flash implementierte Methode, der wir lediglich die entsprechenden Paramter, nämlich die Seitenlänge der Gegenkathete und der Ankathete, übergeben müssen, um den gesuchten Winkel a zu erhalten. Zum Schluß bleibt noch ein kleines Problem: Math.atan2 gibt Werte in Bogenmaß (oder Radiant) zurück, Flash verlangt aber nach Grad. Die Umrechnung erfolgt einfach durch eine Multiplikation des Ergebnisses mit 180/Math.PI.

Für Flash ergibt sich daraus zur Berechnung eines Winkels die Formel:

objekt._rotation = Math.atan2(zielpunkt._y- objekt._y, zielpunkt._x- objekt._x)*180/Math.PI;

  1. Sie können das testen, indem Sie anfangs einen festen Startwert zuweisen. Schreiben Sie unter das bisherige Skript:

pfeil._rotation = Math.atan2(270- pfeil._y, 200- pfeil._x)*180/Math.PI;

Wenn die Anordnung Ihrer Objekte mit meiner identisch ist, wird der Pfeil beim Testen ungefähr in Richtung von Navigationspunkt 4 zeigen.

  1. Da der Pfeil anfangs jedoch auf Navigationspunkt 1 zeigen soll, löschen Sie die eben eingefügte Zeile oder kommentieren sie aus mit vorangestellten //.
  2. Ergänzen Sie das rollOver-Ereignis innerhalb der for-Schleife um folgende Befehle:

rotat = pfeil._rotation;
ziel = Math.atan2(this._y-pfeil._y, this._x-pfeil._x)*180/Math.PI;
pfeil.onEnterFrame = function() {
          rotat = this._rotation;
          this._rotation += ((ziel-rotat)/5);
}

Der Pfeil bewegt sich nun bei RollOver in Richtung des aktivierten Buttons und verlangsamt dabei ständig seine Bewegung.

Zunächst wird die aktuelle Rotation in der Variablen rotat erfasst, was die Berechnung der Geschindigkeit, mit der die Drehung erfolgt, erleichtert. Anschließend wird der zu erreichende Winkel nach der vorher erwähnten Formel in die Variable ziel geschrieben. Um nicht direkt auf den Zielwinkel gesetzt zu werden, muß der Wert, der im Enterframe zur aktuellen Rotation hinzuaddiert wird, natürlich kleiner sein als die Differenz zwischen aktuellem Winkel des Pfeils und Zielwinkel. Dafür sorgt der Klammerausdruck (ziel-rotat)/5. Je höher der Divisor, desto langsamer wird natürlich auch die Bewegung.

  1. Testen Sie verschiedene Geschwindigkeiten, indem Sie die 5 nacheinander durch die 25, dann durch die 2 ersetzen.
  2. Anschließend wählen Sie wieder die 5. Welchen Wert Sie hier konkret einsetzen, hängt allerdings einzig von Ihrem Geschmack ab.

Das Zurücksetzen des Pfeils bei einem Rollout funktioniert nach dem gleichen Prinzip, nur das hier der Zielwinkel nicht mehr errechnet werden muß. Wir können ihn einfach mit 0 vorgeben, denn der Pfeil soll dann horizontal nach rechts zeigen, was im Einheitskreis dem Winkel 0 entspricht.

  1. Ergänzen Sie die for-Schleife um die fettgedruckten Zeilen:

for (e in aBtns) {
          this[aBtns[e]].dot.stop();
          this[aBtns[e]].onRollOver = function() {
                    this.dot.gotoAndStop(2);
                    rotat = pfeil._rotation;
                    ziel = Math.atan2(this._y-pfeil._y, this._x-pfeil._x)*180/Math.PI;
                    pfeil.onEnterFrame = function() {
                              rotat = this._rotation;
                              this._rotation += ((ziel-rotat)/5);
                    };
          };

          this[aBtns[e]].onRollOut = function() {
                    this.dot.gotoAndStop(1);
                    rotat = pfeil._rotation;
                    ziel = 0;
                    pfeil.onEnterFrame = function() {
                              rotat = this._rotation;
                              this._rotation += ((ziel-rotat)/5);
                    };
          };
}

Beim Testen wird der Pfeil nach einem Rollout wieder zurück in die Ausgangsposition gleiten.

Die Funktionsweise entspricht weitgehend dem Rollover-Ereignis. Lediglich der orangene Punkt wird wieder in den blauen verwandelt, und der Zielwinkel wird fest vorgegeben.

Im Grunde genommen könnte man es damit bewenden lassen. Allerdings würde dann das enterFrame-Ereignis endlos weiterlaufen, da wir es nicht löschen. Also selbst dann, wenn der Pfeil bereits längst sein Ziel erreicht hat, führt Flash das enterFrame-Ereignis mit den zugeweisenen Befehlen aus.

  1. Sie können das testen, indem Sie sowohl im rollOver wie im rollOut jeweils nach this._rotation += ((ziel-rotat)/5); schreiben:

trace(„enterFrame“);

  1. Daher sollte man möglichst diejenigen Ereignisse, die nicht mehr benötigt werden, löschen. Schreiben Sie in der for-Schleife sowohl innerhalb des rollOver-Ereignisses wie auch des rollOut-Ereignisses nach trace(„enterFrame“); folgende Zeilen:

if (Math.abs(this._rotation-ziel)<=0.2) {
          this._rotation = ziel;
          delete this.onEnterFrame;
}

Jetzt wird das enterFrame-Ereignis gelöscht, sobald die Differenz zwischen aktuellem Winkel und Zielwinkel kleiner oder gleich 0.2 ist. Das können Sie daran erkennen, daß im Nachrichtenfenster nur noch solange etwas ausgegeben wird, wie sich der Pfeil in Bewegung befindet. Der Grenzwert 0.2 wurde willkürlich gewählt; er sollte jedoch möglichst klein sein, da es ansonsten im letzten Schritt der Animation zu einem kleinen Sprung kommt. Alternativ kann man auch (ziel-rotat)/5 gegen einen sehr niedrigen Grenzwert evaluieren.

Die Methode Math.abs gibt den absoluten Wert eines Ausducks wieder, also in jedem Fall eine positive Zahl. Daher müssen wir nicht darauf achten, welche der beiden als Parameter mitgegebenen Werte – aktuelle Rotation und Zielwinkel – der größere ist.

Wenn wir uns das Skript anschauen, dann fällt auf, daß beim Rollover und beim Rollout nahezu identische Aktionen auszuführen sind. Verschieden ist lediglich der Wert für den Zielwinkel. Das schreit geradezu nach einer parametrisierten Funktion.

Ändern Sie das Skript wie folgt (Fettdruck):

aBtns = [];
for (i=1; i<=5; i++) {
          aBtns.push("b_"+i);
}
for (e in aBtns) {
          this[aBtns[e]].dot.stop();
          this[aBtns[e]].onRollOver = function() {
                    this.dot.gotoAndStop(2);
                    drehen(Math.atan2(this._y-pfeil._y, this._x-pfeil._x)*180/Math.PI);
          };
          this[aBtns[e]].onRollOut = function() {
                    this.dot.gotoAndStop(1);
                    drehen(0);
          };
}
function drehen(winkel) {
          rotat = pfeil._rotation;
          pfeil.onEnterFrame = function() {
                    rotat = this._rotation;
                    this._rotation += ((winkel-rotat)/5);
                    if (Math.abs(this._rotation-winkel)<=0.2) {
                              this._rotation = ziel;
                              delete this.onEnterFrame;
                    }
          };
}

Natürlich läßt sich das hier vorgestellte Skript mit einigen Änderungen auch für andere Anwendungen einsetzen. Versuchen Sie doch mal, die Augen eines Smilies dem Mauszeiger folgen zu lassen (nach dem Motto „Smiling brother is watching you!“).


DVD-Werbung
Kommentare
Achtung: Du kannst den Inhalt erst nach dem Login kommentieren.
Alternative Portrait

-versteckt-(Autor hat Seite verlassen)

  • 23.05.2009 - 20:20

Perfetto :-D

hab mit den Formeln einem MotherShip beigebracht auf den Defender zu schiessen :-D

thx a lot

Gruss Marc el_lobo_66

Alternative Portrait

-versteckt-(Autor hat Seite verlassen)

  • 01.10.2007 - 14:57

lange gesucht und endlich gefunden nur mal schaun ob ichs zu laufen bekomme geht ja über Einsteiger hinaus ist aber sehr gut erklärt

Portrait von rebos
  • 06.11.2006 - 09:07

danke für dieses tolle tut
wirklich gut erklärt, auch wenn ich nicht alles so richtig versteh ;)

Alternative Portrait

-versteckt-(Autor hat Seite verlassen)

  • 30.09.2005 - 21:42

Bitte mehr!!!<br>
<br>
Bisher sehr gute Workshops mit tollem Aufbau und Darstellung von dir gesehen. Genau so soll es sein. Bin gespannt, was uns in Zukunft noch alles erwartet.

Portrait von bassist
  • 22.09.2008 - 22:48

leider nicht =( hänge an dem Button =(

Portrait von Whykiki
  • 30.09.2005 - 10:45

Hallo eine Frage habe ich dazu noch:<br>
<br>
Ich möchte nun, dass der Zeiger nicht auf dem ersten Button startet, sondern seine ursprüngliche Ausgangsstellung soll genau senkrecht nach unten zeigen. Wie bewerkstellige ich das???

Portrait von h_seldon
  • 30.09.2005 - 11:40

Hallo,<br>
<br>
weise dem pfeil ganz am Ende des Skripts eine Drehung um 90 Grad zu:<br>
<br>
pfeil._rotation = 90;<br>
<br>
Wenn er sich nach rollout aus den Navigationspunkten auch wieder auf diese Position hin ausrichten soll, dann mußt Du im entsprechenden Ereignis die Funktion mit dem gewünschten Winkel aufrufen, also:<br>
<br>
drehen(90);<br>
<br>
anstatt wie bisher drehen(0).<br>
<br>
viel spass

Portrait von chud
  • 29.09.2005 - 18:56

niveau: einsteiger?<br>
wusste gar nicht, das die flasheinsteiger von heute gleich mit trigonometrischen funktion anfangen..<br>
<br>
tolles tut, sehr ausfühlich erklärt... da kann man nur alle sterne geben! freu mich schon aufs ausprobieren!

Portrait von h_seldon
  • 30.09.2005 - 08:33

Hallo,<br>
<br>
ja, Du hast Recht, als Niveau müßte da Fortgeschrittene statt Einsteiger stehen. In meiner Tutorial-Vorlage steht standardmäßig Einsteiger; ist mir hier leider durch gerutscht - sorry.<br>
Aber ich hoffe, es ist so ausführlich erklärt, daß man trotzdem zu recht kommt.<br>
<br>
Beste Grüße

Portrait von Whykiki
  • 29.09.2005 - 13:23

SAUBER!!!<br>
<br>
Ich danke dir vielmals, super erklärt, ich werd's zuhause direkt mal versuchen umzusetzen.<br>
Du hast mir wirklich enorm geholfen!!!!<br>
<br>
*lol* Ich hoffe jetzt baut nicht jeder so'n Menü...

x
×
×