Benutzer-Werkzeuge

Webseiten-Werkzeuge


api:documentation:grafik:animation

Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.


api:documentation:grafik:animation [2024/08/31 12:03] (aktuell) – angelegt - Externe Bearbeitung 127.0.0.1
Zeile 1: Zeile 1:
 +====== Animation von Grafikobjekten (die act-Methode) ======
 +<WRAP center round info 100%>
 +Alle modernen Game-[[https://de.wikipedia.org/wiki/Programmierschnittstelle|APIs]] besitzen eine [[https://gameprogrammingpatterns.com/game-loop.html|Game-Loop]], die fortwährend läuft und mit konstanter Frequenz (i.d.R. 60-mal pro Sekunde) eine Methode zur Berechnung des neuen Spielzustands aufruft ([[https://gameprogrammingpatterns.com/update-method.html|Update Method]]). Diese wiederum ruft die Update-Methode aller Spielobjekte auf, so dass auch diese ihren neuen Zustand berechnen können. Anschließend wird der Bildschirm gelöscht und alle graphischen Objekte werden neu gezeichnet (Rendering). \\ \\ 
 +Auch in unserer Programmier-API gibt es diese Game-Loop. Sie wird 30-mal pro Sekunde aufgerufen und ruft ihrerseits die Update-Methode aller Spielobjekte auf, genauer: Diese Methode heißt ''act'' und ist in allen Klassen enthalten, die Unterklassen der Klasse ''Actor'' sind, insbesondere auch in allen Grafikklassen (''Rectangle'', ''Circle'', ...). \\ \\ 
 +Wenn wir also eine Klasse schreiben, die von einer Grafikklasse (oder direct von der Klasse ''Actor'') erbt, können wir diese ''act''-Methode mit unserer eigenen ''act''-Methode überschreiben, so dass dann **diese** 30-mal pro Sekunde aufgerufen wird. \\ \\ 
 +Klingt kompliziert? Keine Angst, anhand der folgenden Beispiele lernst Du schnell, wie es geht und welche großartigen Möglichkeiten sich Dir damit eröffnen! 
 +</WRAP>
 +
 +<HTML>
 +<div style="clear: both"></div>
 +</HTML>
 +===== Beispiel 1: Drehendes Rechteck =====
 +  * Starte das Programm und regle die Geschwindigkeit hoch/runter. -> Die Winkelgeschwindigkeit des Rechtecks ändert sich nicht, da die ''act''-Methode mit konstanter Frequenz (30-mal pro Sekunde) aufgerufen wird.
 +  * Die Bewegung des Rechtecks ist sehr weich (ohne Ruckeln), da die ''act''-Methode im Gleichtakt mit der Render-Methode (s.o.) aufgerufen wird.
 +<HTML>
 +<div class="java-online" style="height: 350px; width: 100%" data-java-online="{'withBottomPanel': false, 'id': 'rotatingRectangle', 'speed': 'max'}">
 +<script type="text/plain" title="rotatingRectangle.java">
 +new World(1000, 1000);
 +new RotatingRectangle(300, 400, 400, 200);
 +// Sobald ein Programm eine act-Methode überschreibt, weiß der Interpreter, dass er nach Ende des
 +// Hauptprogramms mit der Ausführung nicht abbrechen darf, sondern regelmäßig die act-Methoden aufrufen
 +// muss. Die folgende Zeile ist daher unnötig.
 +//while(true){ }
 +
 +class RotatingRectangle extends Rectangle {
 +   
 +   public void act() {
 +      rotate(2);
 +   }
 +
 +}
 +</script>
 +</div>
 +</HTML>
 +
 +===== Beispiel 2: Hin- und herbewegende Rechtecke =====
 +  * Das ''MovingRectangle''-Objekt muss jederzeit "wissen", ob es sich gerade unterwegs zum rechten oder linken Rand befindet. Es benötigt daher ein Attribut, das diesen Zustand speichert. Wir verwenden das Attribut ''int dx'', in dem wir speichern, um wie viel sich das Rechteck im nächsten Schritt in x-Richtung bewegt. Ist ''dx == -8'', so bewegt sich das Rechteck nach links, ist ''dx == 8'', so bewegt sich das Rechteck nach rechts.
 +<HTML>
 +<div class="java-online" style="height: 350px; width: 100%" data-java-online="{'withBottomPanel': false, 'id': 'movingRectangles', 'speed': 'max'}">
 +<script type="text/plain" title="movingRectangle.java">
 +new World(1000, 1000);
 +
 +for(int i = 0; i < 10; i++){
 +   new MovingRectangle(100*i);
 +}
 +
 +class MovingRectangle extends Rectangle {
 +   
 +   int dx = -8;
 +
 +   public MovingRectangle(double y) {
 +      super(Math.random()*800 + 100, y, 80, 80);
 +      setFillColor(Color.lightcyan);
 +   }
 +
 +   public void act() {
 +      move(dx, 0);
 +      if(getCenterX() < 100) {
 +         dx = 8;     // Umkehren am linken Rand
 +      } else if(getCenterX() > 900) {
 +         dx = -8;    // Umkehren am rechten Rand
 +      }
 +   }
 +
 +}</script>
 +</div>
 +</HTML>
 +
 +===== Beispiel 3: Fliegende Bälle =====
 +Jetzt wird's Zeit für ein anspruchsvolleres Beispiel: Wir wollen Bälle simulieren, die vom der Mitte des unteren Bildschirmrandes aus hochgeworfen werden und sich unter dem Einfluss der Gravitationskraft möglichst "natürlich" bewegen. Dazu nutzen wir die [[https://www.leifiphysik.de/uebergreifend/allgemeines-und-hilfsmittel/ausblick/methode-der-kleinen-schritte|Methode der kleinen Schritte]]. Sie bedeutet in unserem Fall, dass wir jede 30-tel Sekunde die neuen Werte für die Position ($x_{neu}, y_{neu}$) und die Geschwindigkeit ($v_{x, neu}, v_{y, neu}$) aus den alten Werten $x, y, vx, vy$ folgendermaßen berechnen:
 +$$v_{x, neu} = v_x$$
 +$$v_{y, neu} = v_y - g\cdot\Delta t$$ 
 +$$x_{neu} = x + v_{x, neu}\cdot\Delta t$$ 
 +$$y_{neu} = y + v_{y, neu}\cdot\Delta t$$ 
 +Das $g$ in der zweiten Gleichung ist die Erdbeschleunigung $g = 9,81\frac{m}{s^2}$. Aus dem "-" wird im Programm ein "+", da die Y-Achse nach unten zeigt. $\Delta t$ ist in unserem Fall $\frac{1}{30} s$.\\ \\
 +Als Spieleprogrammierer wollen wir natürlich, dass der Algorithmus möglichst schnell abläuft. Wir setzen daher $$\Delta t = 1 s$$ und passen dafür die Anfangsgeschwindigkeit und die Beschleunigung etwas an. Da sich $v_x$ nicht ändert, ist bei jedem Zeitschritt also nur noch folgendes zu tun:
 +$$v_{y, neu} = v_y + g$$
 +$$x_{neu} = x + v_x$$ 
 +$$y_{neu} = y + v_{y, neu}$$ 
 +Als Physiklehrer überfällt mich ein großes Unbehagen, wenn ich an die Einheiten der drei obigen Gleichungen denke. Stellt Euch daher rechts jeweils "$\cdot 1 s$ vor, damit Ihr wieder ruhig schlafen könnt ;-) Als Programmierer freue ich mich aber über die einfache und performante Umsetzung. Die zweite und dritte Gleichung zusammengenommen lassen sich jetzt nämlich mit einer einzigen Java-Anweisung erledigen: ''move(vx, vy)''. \\ \\ 
 +=== Erzeuger-Objekt ===
 +Im [[#beispiel_2hin-_und_herbewegende_rechtecke|Beispiel mit den hin- und herbewegenden Rechtecken]] haben wir die neuen Rechtecke im Hauptprogramm erzeugt. Das ist problematisch, da bei vielen Laufenden actor-Methoden nicht sichergestellt ist, mit welcher Geschwindigkeit das Hauptprogramm durchlaufen wird. Stell Dir vor, in einem Spiel würde die Rate, mit der vom Rechner Gegner erzeugt werden, von der Geschwindigkeit Deines Rechners abhängen! \\ Wir wollen es diesmal besser machen und erledigen die Erzeugung der Bälle ebenfalls in einem Ereignis-Handler. Dazu erstellen wir die Klasse ''Erzeuger'' als Unterklasse von ''Actor''. Letztere besitzt eine ''act''-Methode, die 30-mal pro Sekunde aufgerufen wird. Wir überschreiben sie mit einer eigenen Methode, die bei jedem fünften Aufruf einen neuen Ball erzeugt.
 +
 +<HTML>
 +<div class="java-online" style="height: 600px; width: 100%" data-java-online="{'withBottomPanel': false, 'id': 'flyingCircles', 'speed': 'max'}">
 +<script type="text/plain" title="flyingCircles.java">
 +new World(800, 1000);
 +new Erzeuger();
 +
 +class Erzeuger extends Actor {
 +   int zähler = 0;
 +   public void act() {
 +      zähler++;
 +      if(zähler == 5) {          // Jede 1/6-Sekunde (5 * 1/30 = 1/6) wird...
 +         new FliegenderBall();   // ein neuer Ball erzeugt!
 +         zähler = 0;
 +      }
 +   }
 +}
 +
 +class FliegenderBall extends Circle {
 +   
 +   double vx = Math.random() * 10 - 5;    // Geschwindigkeit in x-Richtung in Pixel je 1/30 Sekunde
 +   double vy = Math.random() * (-15) - 5; // Geschwindigkeit in y-Richtung (negativ, da die y-Achse nach unten zeigt!)
 +
 +   public FliegenderBall() {
 +      super(400, 950, 20);       // Kreis mit Radius 20 und Mittelpunkt bei (400;950)
 +      setFillColor(Math.floor(Math.random() * 0xffffff)); 
 +   }
 +
 +   public void act() {
 +      vy += 0.2;        // Die Gravitation beeinflusst die y-Geschwindigkeit (N.B.: y-Achse zeigt nach unten!)
 +      move(vx, vy);     // Methode der kleinen Schritte!
 +      if(isOutsideView() && getCenterY() > 500) {
 +         destroy();     // Falls der Kreis sich außerhalb des Grafikbereichs befindet und sicher nicht mehr darüber, dann zerstöre ihn.
 +      }
 +   }
 +
 +}
 +</script>
 +</div>
 +</HTML>
 +
 +===== Beispiel 4: Feuerwerk =====
 +Dieses Beispiel habe ich als Wiederholungsübung für meine zehnten Klassen im Schuljahr 2020/21 geschrieben. Damals war wegen er Corona-Epidemie das Sylvesterfeuerwerk ausgefallen.
 +{{ youtube>4OY-p4M4hpY?large }}
 +<HTML>
 +<div class="java-online" style="height: 600px; width: 100%" data-java-online="{'withBottomPanel': false, 'id': 'Feuerwerk', 'speed': 'max'}">
 +<script type="text/plain" title="Feuerwerk.java">
 +new Feuerwerk();
 +
 +class Feuerwerk extends Actor {
 +   
 +   public void act() {
 +      if(Math.random() < 0.03) {
 +         
 +         int funkenzahl = Math.floor(Math.random() * 50 + 30);
 +         int farbe = Color.randomColor(128);
 +
 +         double x = Math.random() * 400 + 200;
 +         double y = Math.random() * 600;
 +         double lebensdauer = 60 + Math.random() * 60;
 +         for(int i = 0; i < funkenzahl; i++) {
 +            new Funke(x, y, farbe, lebensdauer);
 +         }
 +         Sound.playSound(Sound.cannon_boom);
 +
 +      }
 +   }
 +
 +}
 +
 +class Funke extends Circle {
 +   double vx;
 +   double vy;
 +   double lebensdauer;           // lebensdauer in 1/30 s
 +
 +   Funke(double x, double y, int farbe, double lebensdauer) {
 +      super(x, y, 4);
 +      double winkel = Math.random() * 2 * Math.PI;
 +      double v = Math.random() * 15 + 5;
 +      vx = v * Math.cos(winkel);
 +      vy = v * Math.sin(winkel);
 +      setFillColor(farbe);
 +      this.lebensdauer = lebensdauer;
 +   }
 +
 +   public void act() {
 +      lebensdauer--;
 +      move(vx, vy);
 +      vy = vy + 0.2;
 +      if(lebensdauer < 30) {
 +         setAlpha(lebensdauer / 30);
 +      }
 +      if(isOutsideView() || lebensdauer < 0) {
 +         destroy();
 +      }
 +   }
 +
 +}
 +</script>
 +</div>
 +</HTML>
  
api/documentation/grafik/animation.txt · Zuletzt geändert: 2024/08/31 12:03 von 127.0.0.1