/* * Engine Alpha ist eine anfängerorientierte 2D-Gaming Engine. * * Copyright (c) 2011 - 2014 Michael Andonie and contributors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package ea; import ea.internal.ani.Animierer; import ea.internal.ani.GeradenAnimierer; import ea.internal.ani.KreisAnimierer; import ea.internal.ani.StreckenAnimierer; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * Der Animationsmanager handelt benutzerfreundlich einfache Animationen. * <p/> * Er arbeitet intern mit unterschiedlichen Objekten der Klasse Animierer.<br /> Es können * theoretisch problemlos auch mehrere Animationen gekoppelt werden. Denn diese arbeitet nicht mit * {@link ea.Raum#positionSetzen(Punkt)} sondern mit relativen Verschiebungen über die Methode * {@link ea.Raum#verschieben(Vektor)}. * * @author Michael Andonie * @see Animierer */ public class AnimationsManager extends Manager implements AnimationsEndeReagierbar { /** * Animationsmanager-Singleton */ private static AnimationsManager instanz; /** * Eine Liste mit allen aktive Animierern. */ private List<Animierer> animierer = new CopyOnWriteArrayList<>(); /** * Konstruktor. Nur Intern benutzt, da Singleton. */ private AnimationsManager () { super(); } /** * Beendet sämtliche laufenden Animationen */ @API public static void neutralize () { getAnimationsManager().alleAbmelden(); } /** * Diese Methode gibt den <b>einen und einzigen existierenden</b> Animationsmanager aus.<br /> * Dies ist ein realisiserter <i>Singleton</i>. * * @return Der eine aktive Animationsmanager. */ @API public static AnimationsManager getAnimationsManager () { if (instanz == null) { instanz = new AnimationsManager(); } return instanz; } /** * Bis ins Lächerliche vereinfachte Methode zum zum Kreisanimieren.<br /> Hierbei wird nicht nur * die gesamte Bewegung automatisch wiederholt und die Umlaufzeit auf 1,5 Sekunden * voreingestellt, sondern auch noch der Mittelpunkt der Kreisbewegung automatisch ermittelt. Er * wird sich 150 Koordinatenpunkte unterhalb des Mittelpunktes des zu animierenden Raum-Objektes * befinden. * * @param ziel * Das zu animierende Raum-Objekt. Hierbei ist dessen Zentrum auf der Kreisbahn. * * @see #kreisAnimation(Raum, Punkt, boolean, int) * @see #kreisAnimation(Raum, Punkt, int) * @see #kreisAnimation(Raum, Punkt) */ @API @SuppressWarnings ( "unused" ) public void kreisAnimation (Raum ziel) { kreisAnimation(ziel, unterhalb(ziel)); } /** * Extrem vereinfachte Methode zum zum Kreisanimieren.<br /> Hierbei wird nicht nur die gesamte * Bewegung automatisch wiederholt, sondern auch die Umlaufzeit auf 1,5 Sekunden * voreingestellt. * * @param ziel * Das zu animierende Raum-Objekt. Hierbei ist dessen Zentrum auf der Kreisbahn. * @param zentrum * Das Zentrum des Animationskreises * * @see #kreisAnimation(Raum, Punkt, boolean, int) * @see #kreisAnimation(Raum, Punkt, int) */ @API public void kreisAnimation (Raum ziel, Punkt zentrum) { kreisAnimation(ziel, zentrum, 1500); } /** * Interne Berechnungsmethode für die absolut standiesierte Methode von {@link * ea.AnimationsManager#kreisAnimation(Raum)}. * * @param m * Das Raum-Objekt, nach dem gerechnet wird. * * @return ein Punkt 150 Pixel unter dem Zentrum der Figur */ private static Punkt unterhalb (Raum m) { return m.zentrum().verschobenerPunkt(new Vektor(0, -150)); } /** * Vereinfachte Methode zum Kreisanimieren.<br /> Hierbei wird die gesamte Bewegung automatisch * wiederholt. * * @param ziel * Das zu animierende Raum-Objekt. Hierbei ist dessen Zentrum auf der Kreisbahn. * @param zentrum * Das Zentrum des Animationskreises * @param umlaufzeit * Gibt in <b>Millisekunden</b> an, wie lange eine Umdrehung um das Zentrum dauern soll.<br /> * <b>ACHTUNG:</b><br /> Dieser Wert muss groesser sein als <b>200</b>, da intern eine * Umdrehung 200 Einzelschritte hat. * * @see #kreisAnimation(Raum, Punkt, boolean, int) */ @API public void kreisAnimation (Raum ziel, Punkt zentrum, int umlaufzeit) { kreisAnimation(ziel, zentrum, true, umlaufzeit); } /** * Animiert ein Raum-Objekt auf einer Kreisbahn. * * @param ziel * Das zu animierende Raum-Objekt. Hierbei ist dessen Zentrum auf der Kreisbahn. * @param zentrum * Das Zentrum des Animationskreises * @param loop * Gibt an, ob die Animation einfach ist oder nicht. * @param umlaufzeit * Gibt in <b>Millisekunden</b> an, wie lange eine Umdrehung um das Zentrum dauern soll.<br /> * <b>ACHTUNG:</b><br /> Dieser Wert muss groesser sein als <b>200</b>, da intern eine * Umdrehung 200 Einzelschritte hat. */ @API public void kreisAnimation (Raum ziel, Punkt zentrum, boolean loop, int umlaufzeit) { this.kreisAnimation(ziel, zentrum, loop, umlaufzeit, true); } /** * Animiert ein Raum-Objekt auf einer Kreisbahn. * * @param ziel * Das zu animierende Raum-Objekt. Hierbei ist dessen Zentrum auf der Kreisbahn. * @param zentrum * Das Zentrum des Animationskreises * @param loop * Gibt an, ob die Animation einfach ist oder nicht. * @param umlaufzeit * Gibt in <b>Millisekunden</b> an, wie lange eine Umdrehung um das Zentrum dauern soll.<br /> * <b>ACHTUNG:</b><br /> Dieser Wert muss groesser sein als <b>200</b>, da intern eine * Umdrehung 200 Einzelschritte hat. */ @API public void kreisAnimation (Raum ziel, Punkt zentrum, boolean loop, int umlaufzeit, boolean uhrzeigersinn) { final KreisAnimierer k = new KreisAnimierer(ziel, zentrum, umlaufzeit, loop, this, this, uhrzeigersinn); animierer.add(k); k.starten(); } /** * Berechnet eine Zahl, die entweder die Eingabe selbst oder 1 ist, sofern die Eingabe kleiner * als 1 ist. * * @param z * Die einzugebende Zahl * * @return 1, wenn <code>z < 1</code>, sonst <code>z</code>. */ @NoExternalUse public static float intervall (float z) { return Math.max(z, 1); } /** * Leicht vereinfachte Form der Streckanimationsmethode.<br /> Hierbei wird, sofern wiederholt * wird, automatisch in einem Kreislauf animiert. * * @param ziel * Das zu animierende Raum-Objekt. Sein Zentrum (ueber die Methode <code>zentrum()</code>) * wird die Strecke abwandern (und natuerlich mit ihm die ganze Figur). * @param laufDauer * Die Zeit <b>in Millisekunden</b>, die vergeht, bis die Bewegung dieser Animation alle * "Ettapen"-Punkte einmal abgegangen ist. * @param wiederholen * Gibt an, ob diese Animation in Dauerschleife wiederholt werden soll, oder ob sie nur einmal * bis zum letzten Punkt sich abspielen soll, und anschliessend sich automatisch selbst * beenden soll. * @param strecke * Eine beliebige Anzahl von "Ettappen"-Punkten. Das Zielobjekt wird sich zwischen diesen * bewegen, wobei die Bewegung zwischen 2 Punkten immer gleich lang ist, unnabhaengig von * ihrer Entfernung zueinander!<br /> Die Erste Ettappe ist das aktuelle Zentrum des * Zielobjekts. * * @see #streckenAnimation(Raum, int, boolean, boolean, Punkt...) */ @API @SuppressWarnings ( "unused" ) public void streckenAnimation (Raum ziel, int laufDauer, boolean wiederholen, Punkt... strecke) { streckenAnimation(ziel, laufDauer, wiederholen, wiederholen, strecke); } /** * Animiert ein Raum-Objekt auf einer Strecke aus einer bestimmten Zahl linearer Teilstrecken. * <p/> * Ein <b>Anwendungsbeispiel:</b> * <p/> * <code> //Instanziiertes, nicht null-wertiges Raum-Objekt.<br /> Raum objekt;<br /> <br /> * //Erstellen eines Managers. In der Klasse "Game" wird bereits einer bereitgestellt (Muss also * dort nicht extra instanziiert werden!!)<br /> AnimationsManager manager = new * AnimationsManager();<br /> <br /> //Ausführen der Methode des Managers<br /> * manager.streckenAnimation(objekt, 4000, true, true, new Punkt(100, 100), new Punkt(200, 100), * new Punkt(200, 200));<br /> </code><br /> Hierbei wird das Objekt auf der Strecke zwischen<br * /> - Seinem Mittelpunkt<br /> - Dem Punkt (100|100)<br /> - Dem Punkt (200|100)<br /> - Und * dem Punkt (200|200)<br /> bewegt und anschliessend wieder <b>zurueck zu seinem alten * Mittelpunkt</b>, da sowohl <code>wiederholen</code> als auch <code>geschlossen true</code> * ist. <br /> Diese Bewegung ("einmal den Kreislauf") dauert 4 Sekunden (= 4000 * Millisekunden). * * @param ziel * Das zu animierende Raum-Objekt. Sein Zentrum (ueber die Methode <code>zentrum()</code>) * wird die Strecke abwandern (und natuerlich mit ihm die ganze Figur). * @param laufDauer * Die Zeit <b>in Millisekunden</b>, die vergeht, bis die Bewegung dieser Animation alle * "Ettapen"-Punkte einmal abgegangen ist. * @param wiederholen * Gibt an, ob diese Animation in Dauerschleife wiederholt werden soll, oder ob sie nur einmal * bis zum letzten Punkt sich abspielen soll, und anschliessend sich automatisch selbst * beenden soll. * @param geschlossen * Gibt an, ob <b>bei Wiederholung</b> die Animation vom letzten wieder zum ersten Punkt * laufen soll oder ob sie vom letzten zum vorletzten (usw) rueckwaerts weiterlaufen soll, bis * zum Anfang und anschliessend wieder wechseln soll, also sozusagen immer wieder <b>hin und * her</b> pendeln soll.<br /> Natuerlich ist dieser Parameter irrelevant, wenn <b>wiederholen * <code>false</code> ist</b>. * @param strecke * Eine beliebige Anzahl von "Ettappen"-Punkten. Das Zielobjekt wird sich zwischen diesen * bewegen, wobei die Bewegung zwischen 2 Punkten immer gleich lang ist, unnabhaengig von * ihrer Entfernung zueinander!<br /> Die Erste Ettappe ist das aktuelle Zentrum des * Zielobjekts. */ @API public void streckenAnimation (Raum ziel, int laufDauer, boolean wiederholen, boolean geschlossen, Punkt... strecke) { final StreckenAnimierer s = new StreckenAnimierer(ziel, wiederholen, geschlossen, this, laufDauer / strecke.length, this, strecke); animierer.add(s); s.starten(); } /** * Vereinfachte Form der Streckanimationsmethode.<br /> Hierbei wird automatisch in einem * geschlossenen Kreislauf wiederholt. * * @param ziel * Das zu animierende Raum-Objekt. Sein Zentrum (ueber die Methode <code>zentrum()</code>) * wird die Strecke abwandern (und natuerlich mit ihm die ganze Figur). * @param laufDauer * Die Zeit <b>in Millisekunden</b>, die vergeht, bis die Bewegung dieser Animation alle * "Ettapen"-Punkte einmal abgegangen ist. * @param strecke * Eine beliebige Anzahl von "Ettappen"-Punkten. Das Zielobjekt wird sich zwischen diesen * bewegen, wobei die Bewegung zwischen 2 Punkten immer gleich lang ist, unnabhaengig von * ihrer Entfernung zueinander!<br /> Die Erste Ettappe ist das aktuelle Zentrum des * Zielobjekts. * * @see #streckenAnimation(Raum, int, boolean, boolean, Punkt...) * @see #streckenAnimation(Raum, int, boolean, Punkt...) */ @API @SuppressWarnings ( "unused" ) public void streckenAnimation (Raum ziel, int laufDauer, Punkt... strecke) { streckenAnimation(ziel, laufDauer, true, true, strecke); } /** * Stark vereinfachte Form der Streckanimationsmethode.<br /> Hierbei wird die Animation * automatisch in einem geschlossenen Kreislauf wiederholt und die Zeit fuer die Bewegung * zwischen den einzelnen Punkten betraegt jeweils eine Sekunde. * * @param ziel * Das zu animierende Raum-Objekt. Sein Zentrum (ueber die Methode <code>zentrum()</code>) * wird die Strecke abwandern (und natuerlich mit ihm die ganze Figur). * @param strecke * Eine beliebige Anzahl von "Ettappen"-Punkten. Das Zielobjekt wird sich zwischen diesen * bewegen, wobei die Bewegung zwischen 2 Punkten immer gleich lang ist, unnabhaengig von * ihrer Entfernung zueinander!<br /> Die Erste Ettappe ist das aktuelle Zentrum des * Zielobjekts. * * @see #streckenAnimation(Raum, int, boolean, boolean, Punkt...) * @see #streckenAnimation(Raum, int, boolean, Punkt...) * @see #streckenAnimation(Raum, int, Punkt...) */ @API @SuppressWarnings ( "unused" ) public void streckenAnimation (Raum ziel, Punkt... strecke) { streckenAnimation(ziel, (strecke.length + 1) * 1000, true, true, strecke); } /** * Noch staerker vereinfachte Variante der <code>geradenAnimation()</code>-Methode.<br /> Der * Weg bis zum <code>orientierung</code>-Punkt wird in <b>einer Sekunde</b> abgegangen, und die * Animation selbst dauert <b>2 Sekunden</b>. * * @param ziel * Das zu animierende Raum-Objekt * @param orientierung * Der Punkt, durch den die Animationslinie verlaeuft. * * @see #geradenAnimation(Raum, Punkt, int, int) * @see #geradenAnimation(Raum, Punkt, int) */ @API @SuppressWarnings ( "unused" ) public void geradenAnimation (Raum ziel, Punkt orientierung) { geradenAnimation(ziel, orientierung, 1000); } /** * Vereinfachte Variante der <code>geradenAnimation()</code>-Methode.<br /> HIer wird bereits * voreingestellt, das die Animation andauert, bis das Objekt den Orientierungspunkt erreicht * hat und noch einmal die selbe Strecke abgegangen ist (sprich, doppelt so lange laufzeit wie * die als Argument mitgegebene <code>zielGeschwindigkeit</code>. * * @param ziel * Das zu animierende Raum-Objekt * @param orientierung * Der Punkt, durch den die Animationslinie verlaeuft. * @param zielGeschwindigkeit * Die <b>Zeit, die vergeht</b> (in millisekunden), bis die Animation den Zielpunkt erreicht. * * @see #geradenAnimation(Raum, Punkt, int, int) */ @API public void geradenAnimation (Raum ziel, Punkt orientierung, int zielGeschwindigkeit) { geradenAnimation(ziel, orientierung, zielGeschwindigkeit, zielGeschwindigkeit * 2); } /** * Animiert ein Objekt auf einer einfachen Halbgerade.<br /> Die Animation endet nach dem Ablauf * ihrer ihr zugesprochenen Dauer (<b>in Millisekunden</b>).<br /> <br /> <b>Ein * Beispiel:</b><br /> <br /> <code> //Das zu animierende, instanziierte Raum-Objekt<br /> Raum * raum;<br /> <br /> //Der AnimationsManager. (Im Zweifelsfall bereits in der Klasse Game als * Variable Vorhanden)<br /> AnimationsManager manager;<br /> <br /> //Die Animation * einleiten<br /> manager.geradenAnimation(raum, new Punkt(300, 200), 1000, 3000);<br /> * </code><br /> Dies erstellt eine Geraden-Animation mit konstanter Geschwindigkeit, die:<br /> * - Vom Mittelpunkt des <code>raum</code>-Animationsobjekts anfaengt<br /> - Durch den Punkt * (300|200) verlaeuft<br /> - Diesen Punkt nach 1000 Millisekunden (= 1 Sekunde) erreicht * und<br /> - 3000 Millisekunden (= 3 Sekunden) andauert, und dann automatisch beendet wird.<br * /> <br /> Diese Methode hat den Vorteil, das sie nie dauerhaft Speicherressourcen verbraucht, * da keine dieser Animationen (im Gegensatz zu zB einer Kreis-Animation) unbegrenzt lange * laufen kann. * * @param ziel * Das zu animierende Raum-Objekt * @param orientierung * Der Punkt, durch den die Animationslinie verlaeuft. * @param zielGeschwindigkeit * Die <b>Zeit, die vergeht</b> (in millisekunden), bis die Animation den Zielpunkt erreicht. * @param dauerInMS * Die Dauer in Millisekunden, bis die Animation beendet wird. ISt diese geringer als die * <code>zielGeschwindigkeit</code>, so erreicht die Animation nicht den Orientierungspunkt * (der Eingabeparamter <code>orientierung</code>) */ @API public void geradenAnimation (Raum ziel, Punkt orientierung, int zielGeschwindigkeit, int dauerInMS) { GeradenAnimierer g = new GeradenAnimierer(ziel, orientierung, zielGeschwindigkeit, dauerInMS, this, this); animierer.add(g); g.starten(); } /** * Beendet <b>alle</b> Animationen von einem Raum-Objekt.<br /> Gibt es keine Animation von * diesem, so passiert <b>gar nichts</b>. * * @param raum * Das Raum-Objekt, dessen Animation(en) von diesem Manager beendet werden soll(en) */ @API public void animationBeendenVon (Raum raum) { for (Animierer a : animierer) { if (raum == a.ziel()) { a.beenden(); animierer.remove(a); } } } /** * Die implementierte <code>endeReagieren</code>-Methode. * <p/> * Hierin wird jede Referenz auf die beendete Animation gelöscht. * * @param an * Die gerade geendetet Animation */ @Override public void endeReagieren (Animierer an) { animierer.remove(an); } }