K (Animationen)
K (Animationen)
 
Zeile 298: Zeile 298:
 
</source>
 
</source>
  
'''Beachte:''' Der Spieler (Traveler) wird vom Gegner (Bob) nach rechts verdrängt, sobald Bob ihm entgegen kommt, da Bob kinematisch ist. Außerdem wird der Spieler (Traveler) von der rechten, rotierenden Plattform mitgenommen, sobald er darauf steht, weil auch diese Plattform kinematisch ist.
+
'''Beachte:''' Der Spieler (Traveler) wird vom Gegner (Bob) nach rechts verdrängt, sobald Bob ihm entgegen kommt, da Bob kinematisch ist. Außerdem wird der Spieler (Traveler) von der rechten, rotierenden Plattform mitgenommen, sobald er darauf steht, weil auch diese Plattform kinematisch ist.<br>
 +
'''Tipp:''' Solltest du feststellen, dass dein Spieler langsam von der rotierenden Plattform herunter gleitet, so denke daran, die Reibung von Plattform und Spieler auf mindestens 1 zu stellen ;-)
  
 
=== Kinematische Physik ===
 
=== Kinematische Physik ===

Aktuelle Version vom 13. Juni 2023, 12:12 Uhr


Dies ist ein Tutorial für die Edu-Variante der Engine Alpha 4.x. Eine Übersicht aller Edu-Tutorials siehst du hier.

Inhalt

In diesem Tutorial:

  • Lernst du gemeinsame Fähigkeiten aller grafisch darstellbaren Objekte kennen
  • Gewinnst du einen Überblick über die Möglichkeiten der eingebauten Physik

Super-Klasse EduActor

EA Demo

Die Super-Klasse EduActor ist im wahrsten Sinne die Mutter aller grafisch darstellbaren Klassen der Edu-Variante der Engine Alpha. Es handelt sich um eine abstrakte Klasse von der man selbst keine Objekte erzeugen kann. Aber in dieser abstrakten Klasse werden sehr viele Methoden und Verhaltensweisen aller grafisch darstellbaren Objekte definiert. Jede Klasse, deren Objekte auf der Bühne sichtbar werden, hat von dieser Klasse geerbt. Das bringt folgende Vorteile:

  • Alle Klassen (KREIS, RECHTECK, DREIECK, FIGUR, TEXT) verfügen über eine Menge an gemeinsamen Methoden.
  • Der Klassen-Bezeichner EduActor kann als Platzhalter für jede der oben genannten Klassen verwendet werden. Wann immer du auf diesen Datentyp z.B. beim Parameter von schneidet(EduActor ea) oder stehtAuf(Eduactor ea) stößt, kannst du beliebige Objekte der oben genannten Klassen einsetzen.


grundlegende Methoden

  • verschieben(double deltaX, double deltaY)
    
    (Maßeinheit ist "Bildschirm-Meter")
  • drehen(double grad)
    
    (in Winkelgrad)
  • setzeDrehwinkel(double grad)
    
    (Maßeinheit ist Winkelgrad)
  • nenneDrehwinkel() : double
    
    (Maßeinheit ist Winkelgrad)
  • setzeMittelpunkt(double x, double y)
    
    (Maßeinheit ist "Bildschirm-Meter". Ursprung in der Mitte der Bühne. Achsen wie in Mathe.)
  • setzeSichtbar(boolean sichtbar)
    
    (sichtbar=true, unsichtbar=false)
  • nenneSichtbar() : boolean
    
    (sichtbar = true, unsichtbar=false)
  • beinhaltetPunkt(double x, double y): boolean
    
    (Maßeinheit ist "Bildschirm-Meter". JA=true, NEIN=false)
  • schneidet(EduActor ea): boolean
    
    (Es kann jedes grafisch darstellbare Objekt übergeben werden. JA=true, NEIN=false)
  • setzeTransparenz(double wert)
    
    (0 = ohne Transparenz, 1 = völlige Transparenz, also unsichtbar)
  • nenneTransparenz() : double
    
    (0 = ohne Transparenz, 1 = völlige Transparenz, also unsichtbar)
  • animiereTransparenz(double sekunden, double transparenzNachher)
    
    (Vom jetzigen Transparenz-Wert wird in der angegebenen Zeit zum neuen Transparenz-Wert "übergeblendet")

Anordnung und Ebenen

Normalerweise werden Objekte, die zuerst angelegt werden weiter hinten gezeichnet als später angelegte Objekte. Das heißt, dass später gezeichnete Objekte früher gezeichnete Objekte überdecken. Man kann aber auch gezielt Ebenen verwenden, um selbst zu definieren, welches Objekt von welchem anderen Objekt verdeckt wird, bzw. das andere Objekt überdeckt. Diese kann auch zur Laufzeit des Programms verändert werden und so z.B. Objekte von vorne nach hinten oder umgekehrt versetzt werden.

Hierzu gibt es die folgenden beiden Methoden:

  • setzeEbenenposition(int positiion)
    
    (0 ist ganz hinten, größere Werte liegen weiter vorne.)
  • nenneEbenenposition() : int
    
    (Objekte mit höherem Wert liegen weiter vorne.)

Hier der nötige Quellcode:

Ball fällt scheinbar in Eimer
public class Test
extends SPIEL
{
    private FIGUR eimer_unten;
    private FIGUR eimer_oben;
    private KREIS ball;
    
    public Test()
    {
        this.eimer_oben = new FIGUR( "Eimer_rot_oben.png" );
        this.eimer_oben.setzeMittelpunkt( 0 , -8+2.6 );
        this.eimer_oben.setzeEbene( 0 ); // hinten
        
        this.eimer_unten = new FIGUR( "Eimer_rot_unten.png" );
        this.eimer_unten.setzeMittelpunkt( 0 , -8 );
        this.eimer_unten.setzeEbene( 2 ); // vorne
        
        this.ball = new KREIS( 1 );
        this.ball.setzeMittelpunkt( 0 , 3 );
        this.ball.setzeFarbe( "gelb" );
        this.ball.setzeEbene( 1 ); // zwischen "hinten" und "vorne"
    }
    
    @Override
    public void tasteReagieren( int code )
    {
        this.ball.macheDynamisch(); // fallen lassen
    }
}

Hier die Bild-Dateien zum Download: Eimer rot oben.png Eimer rot unten.png

Jump'n'Run-Physik

Die Jump'n'Run-Physik ist auf die einfachen Bedürfnisse von Jump'n'Run-Spielen ausgelegt und kennt vier verschiedene Zustände' eines EduActor:

  • sensor : Zeigt keinerlei Wechselwirkung mit anderen EduActor-Objekten, reagiert aber auf Methode schneidet(...).
  • dynamisch : Unterliegt der Schwerkraft und wird von statischen und kinematischen EduActor-Objekten aufgehalten oder kann auf ihnen stehen und von ihnen abspringen.
  • statisch : Stellt für dynamische EduActor-Objekte ein Hindernis bzw. einen Boden dar, kann dynamische EduActor-Objekte verdrängen, verschieben oder mitnehmen. Wie der Name schon sagt, sollten statische Objekte nachträglich nicht mehr verändert werden
  • kinematisch : An sich wie „statisch“. Im Unterschied informieren kinematische Objekte bei Lage-Veränderung die interne Physik, so dass andere Objekte darüber informiert werden und sich entsprechend verhalten.

Frisch erzeugte EduActor-Objekte befinden sich immer im Zustand sensor.

Hierzu werden die folgenden Methoden zur Verfügung gestellt:

  • macheDynamisch()
    
    Hiermit wird ein EduActor-Objekt dynmaisch gemacht. Es unterliegt nun auch der Schwerkraft und kann keine statischen und kinematischen EduActor-Objekte mehr durchdringen.
  • macheStatisch()
    
    Hiermit wird ein EduActor-Objekt statisch gemacht. Es hält nun alle dynamischen EduActor-Objekte auf. Statische Objekte sollten nachträglich nicht mehr den Ort oder die Größe ändern! Hierfür gibt es den Zustand kinematisch.
  • macheKinaematisch()
    
    Hiermit wird ein EduActor-Objekt kinematisch gemacht. Es hält nun alle dynamischen EduActor-Objekte auf bzw. nimmt sie mit indem es sie wegstößt oder mitnimmt, wenn sie darauf stehen.
  • macheSensor()
    
    Hiermit wird ein EduActor-Objekt neutral gemacht. Es nimmt nun nicht mehr an der Jump'n'Run-Physik Teil.
  • steht() : boolean
    
    Überprüft, ob ein dynamisches EduActor-Objekt auf irgendeinem statischen EduActor-Objekt steht.
  • stehtAuf(EduActor ea) : boolean
    
    Überprüft, ob ein dynamisches EduActor-Objekt auf einenm bestimmten statischen EduActor-Objekt steht.
  • springe(double staerke)
    
    Ein dynamisches EduActor-Objekt, das gerade auf irgendeinem statischen EduActor-Objekt steht, kann von diesem abspringen. Also übergebener Wert ist 5 ein guter Startwert.
  • setzeRotationBlockiert(boolean blockiert)
    
    Standardmäßig drehen sich EduActor-Objekte nicht während der Teilnahme an der Physik. ISt die Rotation eingeschaltet, können z.B dynamische EduActor-Objekte, wenn sie auf dem Rand eines statischen EduActor-Objekts stehen, überkippen. Sie können nun auch "umgehauen" werden. Ein Ball bekäme in diesem Fall auch "Spinn".
Jump'n'Run Physik Demo

Demo-Code "Jump'n'Run"

public class JumpRun 
extends SPIEL
{
    private FIGUR spieler , boden_1 , boden_2 ;
    
    public JumpRun()
    {
        for ( int i=0 ; i<20 ; i++ )
        {
            FIGUR boden = new FIGUR( "Wiese.png" );
            boden.setzeMittelpunkt( -20+i*2.1 , -9 );
            boden.macheStatisch();
        }
        
        boden_1 = new FIGUR( "Wiese2.png" );
        boden_1.setzeMittelpunkt( 0.5 , -4 ) ;
        boden_1.macheStatisch();
        
        boden_2 = new FIGUR( "Wiese2.png" );
        boden_2.setzeMittelpunkt( 6.5 , 1 ) ;
        boden_2.macheStatisch();
        
        spieler = new FIGUR( "traveler_idle.gif" ) ;
        spieler.setzeMittelpunkt( -5 , -5 );
        spieler.macheDynamisch();
    }
    
    @Override
    public void tasteReagieren( int code )
    {
        switch( code )
        {
            case TASTE.RAUF :   spieler.springe( 10 ); 
                                break;
            case TASTE.LINKS:   spieler.setzeGeschwindigkeit( -2 , 0 ); 
                                spieler.spiegelnHorizontal( true ) ;
                                break;
            case TASTE.RECHTS:  spieler.setzeGeschwindigkeit( 2 , 0 );  
                                spieler.spiegelnHorizontal( false ) ;
                                break;
            case TASTE.RUNTER:  spieler.setzeGeschwindigkeit( 0 , 0 );
                                break;
        }
    }
}
Ball springt in Eimer

Demo-Code "Simulation"

public class Eimer 
{
    private FIGUR unten;
    private FIGUR oben;
    // unsichtbare "Ränder" des Eimers zum abprallen
    private RECHTECK links;
    private RECHTECK rechts;
    
    Private KREIS ball;
    

    public Eimer( int x , int y )
    {
        
        this.oben = new FIGUR( "Eimer_blau_oben.png" );
        this.oben.setzeMittelpunkt( x , y+2.6 );
        this.oben.setzeEbene( 0 );
        this.unten = new FIGUR( "Eimer_blau_unten.png" );
        this.unten.setzeMittelpunkt( x , y );
        this.unten.setzeEbene( 2 );
        
        // linke Eimer-Kante zum abprallen
        this.links = new RECHTECK( 0.1 , 4 );
        this.links.setzeMittelpunkt( x-3 , y+0.9 );
        this.links.setzeSichtbar( false );
        this.links.setzeDrehwinkel( 11.5 );
        this.links.setzeEbenenposition( 0 );
        this.links.macheStatisch();
        this.links.setzeElastizitaet( 0.5 );
        // rechte Eimer-Kante zum abprallen
        this.rechts = new RECHTECK( 0.1 , 4 );
        this.rechts.setzeMittelpunkt( x+3 , y+0.9 );
        this.rechts.setzeSichtbar( false );
        this.rechts.setzeDrehwinkel( -11.5 );
        this.rechts.setzeEbenenposition( 0 );
        this.rechts.macheStatisch();
        this.rechts.setzeElastizitaet( 0.5 );
        
        this.ball = new KREIS(1);
        this.ball.setzeMittelpunkt( x-2 , y+8 );
        this.ball.macheDynamisch();
    }
}

Animationen

Alle EduActor-Objekte können auch animiert werden. Hierzu stehen folgende Methoden zur Verfügung:

  • animiereTransparenz(double sekunden, double transparenzNachher)
    
    In der angegebenen Zeit wird die Transparenz vom aktuellen Wert zum übergebenen Wert hin verändert.
  • animiereKreis(double sekunden, double mX, double mY, boolean uhrzeigersinn, boolean rotation)
    
    In der angegebenen Zeit wird das Objekt um den angegebenen Mittelpunkt im (true) oder gegen (false) den Uhrzeigersinn rotiert. Das rotierende Objekt kann seine Ausrichtung beibehalten oder mit rotieren wie ein Uhrzeiger.
  • animiereGerade(double sekunden, double zielX, double zielY, boolean loop)
    
    In der angegebenen Zeit wird das Objekt vom aktuellen Mittelpunkt zum übergebenen Ziel-Mittelpunkt auf einer Gerade bewegt. Die Bewegung kann einmalig (false) oder fortwährend hin und wieder zurück zum ursprünglichen Mittelpunkt erfolgen.
  • pausiereAnimation(boolean pause)
    
    Die Animation eines Objekts stoppen und auch wieder erneut starten.
  • nennePausiert() : boolean
    
    Gibt an, ob bei dem Objekt gerade eine Animation pausiert ist.

Animierte statische Objekte können dynamische Objekte mitnehmen! (Demo 2)

Vorsicht !!!
Kinematische Methoden wie setzeGeschwindigkeit(...) oder wirkeImpuls(...) können zwar auch auf dynamischen Objekten aufgerufen werden. Dies führt aber bei Interaktion mit statischen Objekten zu unvorhersehbaren Effekten.
Möchte man dynamischen Objekten durch Code eine Geschwindigkeit zuweisen, so überschreibt man hierzu besser die von SPIEL geerbte Methode bildAktualisierungReagieren(...) und bewegt die Objekte dort unter Anwendung gängiger physikalischer Formeln und Benutzung des automatisch übergebenen Parameters dieser Methode !!!
(siehe Demo 2 unten)

kreisender Zeiger

Demo 1: "rotierender Zeiger"

public class Uhr
{
    private RECHTECK sekundenzeiger;
    
    public Uhr()
    {
        this.sekundenzeiger = new RECHTECK(0.1, 5);
        this.sekundenzeiger.setzeFarbe("gelb");
        this.sekundenzeiger.setzeMittelpunkt(0, 2.5);
        this.sekundenzeiger.animiereKreis(60 , 0 , 0 , true, true);
    }
}

Demo 2: "Jump'n'Run-Animationen"

Effekte durch Animationen
public class Animation
extends SPIEL
{
    private RECHTECK boden, platte_1, platte_2;
    private FIGUR spieler, gegner;
    private double v;
    
    public Animation()
    {
        this.boden = new RECHTECK(40, 1);
        this.boden.setzeMittelpunkt(0, -9);
        this.boden.macheStatisch(); // steht still
        
        this.platte_1 = new RECHTECK(5, 0.5);
        this.platte_1.setzeMittelpunkt(-5, -5);
        this.platte_1.macheStatisch(); // steht still
        
        this.platte_2 = new RECHTECK(4, 0.5);
        this.platte_2.setzeMittelpunkt(5, -5);
        this.platte_2.macheKinematisch(); // da sie sich bewegt
        this.platte_2.animiereKreis(8, 5, -3, true, false);
        
        this.gegner = new FIGUR("standard", ".", "bob");
        this.gegner.setzeMittelpunkt(-7, -3.5);
        this.gegner.macheKinematisch(); // da er sich bewegt
        this.gegner.animiereGerade(3, -3, -3.5, true);
        
        this.spieler = new FIGUR("traveler_idle.gif");
        this.spieler.setzeMittelpunkt(0, -6.5);
        this.spieler.macheDynamisch();
        
        this.v = 0;
    }
    
    @Override
    public void tasteReagieren(int code)
    {
        switch (code)
        {
            case TASTE.RAUF   : this.spieler.springe(10); break;
            case TASTE.RUNTER : this.v = 0.0; break;
            case TASTE.LINKS  : this.v = -2;
                                this.spieler.spiegelnHorizontal(true); break;
            case TASTE.RECHTS : this.v = 2;
                                this.spieler.spiegelnHorizontal(false); break;
        }
    }
    
    @Override
    public void bildAktualisierungReagieren(double time)
    {
        this.spieler.verschiebenUm(this.v*time , 0);
    }
}

Beachte: Der Spieler (Traveler) wird vom Gegner (Bob) nach rechts verdrängt, sobald Bob ihm entgegen kommt, da Bob kinematisch ist. Außerdem wird der Spieler (Traveler) von der rechten, rotierenden Plattform mitgenommen, sobald er darauf steht, weil auch diese Plattform kinematisch ist.
Tipp: Solltest du feststellen, dass dein Spieler langsam von der rotierenden Plattform herunter gleitet, so denke daran, die Reibung von Plattform und Spieler auf mindestens 1 zu stellen ;-)

Kinematische Physik

Du kennst aus der Jump'n'Run-Physik die Zustände sensor, statisch und dynamisch. Es existiert noch ein vierter Zustand kinematisch. Damit verhlält sich ein EduActor-Objekt physiklisch realistisch. Ein kinematisches EduActor-Objekt interagiert mit sensor- oder statisch-Objekten wie aus der Jump'n'Run-Physik gewohnt. JEdoch stoßen sich kinematische Objekte gegenseitig ab, wenn sie sich berühren. Auf kinematischen Objekten kann man aber zusätzliche Methoden aufrufen, um die mechanischen Eigenschaften wie Reibung und Elastizität oder Größen wie Dichte, Masse, Geschwindigkeit und Winkelgeschwindigkeit festzulegen. Außerdem kann ein Impuls (Kraft-Stoß) in beliebige Richtung auf ein Objekt einwirken.

Es stehen folgende Methoden zur Verfügung:

kinematische Objekte, statischer Boden
  • macheKinematisch()
    
    Versetzt das EduActor-Objekt in den kinematischen Zustand.
  • setzeReibung(double reibungsKoeffizient)
    
    Damit wird der Reibungskoeffizient dieses Objekts bei Berührung mit einem anderen kinematischen oder statischen Objekt festgelegt. Diese Änderung wird aber erst dann aktiv, wenn das Objekt kein anderes dynamisches oder statisches Objekt berührt. In diesem Fall tritt der Effekt zeitverzögert in Kraft, sobald die Berührung gelöst wird.
  • nenneReibung() : double
    
    Gibt den aktuellen Reibungskoeffizienten zurück.
  • setzeElastizitaet(double elastizitaet)
    
    Ein Wert von 1 bedeutet einen vollkommen elastischen Stoß. Ein Wert von 0 ein vollkommen inelastischer Stoß. Werte dazwischen sind möglich und können zu sehr realistischen Stößen führen. Werte über 1 sind möglich aber unrealistisch - können aber zu witzigen Effekten führen ...
  • nenneElastizitaet() : double
    
    Gibt den aktuellen Elastizitätswert zurück.
  • setzeGeschwindigkeit(double vX, double vY)
    
    Das Objekt bewegt sich ab jetzt mit dieser Geschwindigkeit und Richtung. Die Maßeinheit ist m/s.
  • nenneGeschwindigkeitX() : double
    
    Gibt die x-Komponente der aktuellen Geschwindigkeit in m/s zurück.
  • nenneGeschwindigkeitY() : double
    
    Gibt die y-Komponente der aktuellen Geschwindigkeit in m/s zurück.
  • setzeDichte(double flaechendichte)
    
    Legt die Dichte des Objekts fest. Da es sich um eine 2D-Game-Engine handelt ist es streng genommen eine Flächendichte, Die Maßeinheit ist deshalb kg/m2. Eine Änderung der Dichte zieht logischerweise auch eine Änderung der Masse nach sich.
  • nenneDichte() : double
    
    Gibt den aktuellen Wert der Dichte des Objekts in kg/m2 zurück.
  • nenneMasse() : double
    
    Gibt den aktuellen Wert der Masse zurück. Die Maßeinheit ist kg. Die Masse kann NICHT direkt gesetzt werden! Stattdessen muss man Dichte und Abmessungen ändern, um die Masse zu verändern.
  • wirkeImpuls(double pX, double pY)
    
    Wirkt einen Impuls (Kraft-Stoß, Schlag, Kick) auf das Objekt aus. Die Maßeinheit ist kg*m/s. Die Wirkung des Impulses auf das Objekt hängt natürlich von dessen Masse ab. Schwerere Objekte verhalten sich "träger" bzw. "schwerfälliger".
  • setzeRotationBlockiert(boolean b)
    
    true bewirkt, dass sich kinematische Objekte bei Stößen nicht in Drehung versetzen, aber auch, dass sie erst von statischen Objekten fallen, wenn sie diese an keinem Punkt mehr berühren.false bewirkt realistisches Verhalten wie Rotationsbewegungen nach Stößen oder Überkippen beim Abstürzen, sobald der Schwerpunkt die Kante eines statischen Objekts überwunden hat.
  • setzeWinkelgeschwindigkeit(double wg)
    
    Setzt die Winkelgeschwindigkeit des Objekts in Umdrehungen pro Sekunde.

individuelle Kollisions-Formen

Beispiele für individuelle Kollisionsformen

Der Kollisions-Bereich der EduActor-Unterklassen KREIS, RECHTECK und DREIECK ist klar definiert. Bei den Unterklassen FIGUR und TEXT ist es dagegen standardmäßig ein umhüllendes Rechteck. Das führt - z.B. bei Bildern mit transparentem Hintergrund nicht immer zu den gewünschten Interaktionen mit anderen Objekten. Deshalb kann man jedem EduActor auch eine individuelle Kollisions-Form - bestehend aus Rechtecken, Kreisen und Polygonen - erstellen.

Hierfür gibt es die Methode

setzeKollisionsformen(String formenDefinition)

Der übergebene String kann ein einzelner Kollisionsbereich oder auch eine Aneinanderreihung mehrerer einzelner Kollisionsbereiche enthalten. Die Maßeinheit ist Bildschirm-Meter und der Koordinatenursprung ist die linke untere Ecke des umhüllenden Rechtecks.

  • R1,0.5,1.2,3 Ein Rechteck mit linker, unterer Ecke (1|0,5), der Breite 1,2 und der Höhe 3
  • C2.5,3,1.75 Ein Kreis (Circle) mit Mittelpunkt (2,5|3), der Radius 1,75
  • P1,0,2,3,0.5,2 Ein Polygon (hier ein Dreieck) mit den Eckpunkten (1|0), (2|3) und (0.5|2)
  • R1.5,0,0.5,2.7 &C1.5,4,1 Rechteck und Kreis kombiniert zu einem "Lolli"

! ! ! WICHTIG ! ! ! Zwischen dem & und dem folgenden Buchstaben darf KEIN Leerzeichen sein! Am Ende einer Form darf KEIN Komma stehen!

TIPP In Verbindung mit setzeRotationBlockiert(false) und der Jump'n'Run-Physik können SpielFiguren z.B. sehr realistisch von Brüstungen fallen ;-)

Rotation nicht blockiert
public class KollisionsFormen
{
    private FIGUR ritter;
    private RECHTECK boden;
    
    public KollisionsFormen()
    {
        SPIEL.zeigeKoordinatensystem( true );
        this.boden = new RECHTECK( 8 , 1 );
        this.boden.setzeMittelpunkt( -8 , 0);
        this.boden.drehen( -3 );
        this.boden.setzeFarbe( "blau" );
        this.boden.macheStatisch();
        
        this.ritter = new FIGUR( "Ritter.png" );
        this.ritter.skaliere( 0.5 );
        this.ritter.setzeKollisionsformen( "C 2.34,4.25,2.1 &P 1.5,0.43, 3.5,0.43, 4.3,1.15, 4.25,2, 3.3,2.38, 1.35,2.38, 0.78,1.5" );
        this.ritter.setzeMittelpunkt( -9 , 5 );
        this.ritter.setzeRotationBlockiert( false );
        this.ritter.macheDynamisch();
    }
}

Verbindungen zwischen Objekten

Es ist auch möglich, mehrere EduActor-Objekte miteinander auf verschiedene Arten zu verbinden.
... Doku hierzu derzeit noch in Arbeit ...

Methoden zeitverzögert ausführen

Es ist auch möglich, jede beliebige Methode eines EduActor-Objekts zeitverzögert und damit nebenläufig zum restlichen Code anzusetzen. Hierfür gibt es in der Klasse SPIEL die Methode parallel(...). Die Syntax für den Übergabe-Parameter ist gewöhnungsbedürftig, da es sich hierbei um einen sogenannten Lambda-Ausdruck handelt.
Das folgende Programm demonstriert die Verwendung von parallel(...):

Methode parallel und zeitverzögert ausführen
public class Test
extends SPIEL
{
    private KREIS ball;
    private RECHTECK boden;
    private DREIECK ding;

    public Test()
    {
        super( 200 , 300 );
        
        this.boden = new RECHTECK( 3 , 0.5 );
        this.boden.setzeMittelpunkt( 0 , 2 );
        this.boden.macheKinematisch();
        
        this.ball = new KREIS( 1 );
        this.ball.setzeMittelpunkt( 0 , 4 );
        this.ball.macheDynamisch();
        
        parallel( () -> { super.warte(3); this.boden.verschiebenUm( 0 , -5 ); } );
        
        this.ding = new DREIECK( 0,0 , 1,0 , 0.5,1 );
        this.ding.setzeMittelpunkt( -2 , -4 );
        this.ding.setzeGeschwindigkeit( 1 , 0 );
    }
}

Die Beispiel-Klasse erbt von SPIEL um über die Methode parallel(...) zu verfügen. Sie soll einen Kreis, ein Rechteck und ein Dreieck darstellen. Das Fenster ist 200px breit und 300px hoch.
Das Rechteck dient als Boden und wird kinematisch gemacht. Damit verhält sich der Boden als ob er statisch wäre, jedoch können sich kinematische Objekte nachträglich bewegen und geben das an die interne Physik weiter, so dass der Ball später "bemerkt", dass der Boden weg ist. (Statische Objekte sollte man - wie der Name schon sagt - nachträglich nicht mehr verschieben, drehen oder vergrößern.)
Oberhalb des Bodens wird ein Ball platziert und dynamisch gemacht, so dass er auf dem Boden zu liegen kommt.
Nun wird der Boden zeitverzögert um 3 Sekunden nach unten verschoben. Der Methode parallel(...) wird hierzu ein Lambda-Ausdruck in den runden Klammern übergeben. Er hat entweder die Form () -> Methodenaufruf; oder () -> {Methodenaufruf_1; Methodenaufruf_2; ... }. Jeder Methodenaufruf MUSS mit einem Strichpunkt abgeschlossen werden! Bei mehreren Methoden sind geschweifte Klammern nötig!
Als letztes wird im Konstruktor das Dreieck erzeugt und nach rechts bewegt.
Man sieht in der animierten Grafik, dass der Boden sich erst 3 Sekunden nach Erscheinen des Dreiecks nach unten bewegt, obwohl der Befehl hierzu vor der Erzeugung und dem Verschieben des Dreiecks angesetzt ist. Die Bewegung des Dreiecks wird also ungehindert ausgeführt, bis die 3 Sekunden um sind, erst dann bewegt sich der Boden und damit der Ball (letzterer aufgrund seines dynamischen Zustands).
Vorsicht: Die Methode parallel(...) darf NIE innerhalb der Methode tick() aufgerufen werden!!! Man kann damit aber z.B. Aktionen nach Tastendruck oder Mausklick verzögert ausführen. Eine weitere Anwendungsmöglichkeit wäre z.B., dass ein Gegner oder eine Schikane zeitverzögert nach dem Betreten eines gewissen Bereichs aktiviert werden soll. Manchmal gelingt es auch einfach nicht, alle parallelen Spiel-Logiken in bildAktualisierungReagieren(...) zu verzahnen. Manchmal kann man einiges in Methoden auslagern und dann mehrere parallele Aufrufe im Code zu starten (ohne die Zeitverzögerung).


Das Tutorial ist beendet. Das nächste ist YYY . Wenn du Feedback für uns hast, melde dich gerne.