Allgemeines
Wir wollen Planetensystem animieren, in welchem Planeten um die Sonne kreisen. Hier werden wir abstrakte Klassen kennen lernen und eine etwas kompliziertere Berechnung für die Animation.
Vorbereitungen
Erstellen Sie die Klassen Stern
, Planet
, Gasplanet
, Gesteinsplanet
und Umlaufbahn
nach dem folgenden Klassendiagramm. Informieren Sie sich ggf. über die Unterschiede zwischen Gesteins- und Gasplaneten.
Das <<abstract>>
in der Klassenkarte von Planet
bedeutet, dass es sich hierbei um eine abstrakte Klasse handelt.
Hinweis: Im Klassendiagramm werden Properties, die nicht unmittelbar in diesem Kapitel behandelt werden weggelassen.
Abstrakte Klasse Planet
Von abstrakten Klassen kann man selbst keine Objekte erzeugen ("man kann sie nicht instanziieren"). Aber wir können von ihnen erben und damit Struktur in unseren Entwurf bringen. Abstrakte Klassen können nämlich neben ganz regulären konkreten Funktionen auch abstrakte Funktionen enthalten (sobald eine Funktion in einer Klasse abstrakt ist, muss die ganze Klasse als abstrakt markiert werden).
Abstrakte Methoden haben keinen Funktionsrumpf (keine Implementierung in den geschweiften Klammern) sondern nur einen Funktionskopf. Alle Subklassen einer abstrakten Klasse müssen dann diese abstrakten Funktionen überschreiben und so eine konkrete Implementierung zur Verfügung stellen (siehe Code-Beispiel)
Man kann also voraussetzen, dass alle Subklassen einer abstrakten Klasse für alle in der Superklasse deklarierten abstrakten Funktionen eine Implementierung besitzen.
Damit haben wir auch schon den Zweck von abstrakten Klassen: Man schafft eine vordefinierte Schnittstelle, der alle Subklassen gehorchen (müssen)
Im Planetenbeispiel macht das Sinn: Jeder Planet in unserem Sonnensystem ist ein Gesteinsplanet
oder ein Gasplanet
. Also werden von diesen Klassen Objekte erstellt. Beide Klassen erben aber von Planet
, da es redundant wäre, wenn man gemeinsames Verhalten oder Zustände (die ja Objekte beider Subklassen auch besitzen) mehrmals deklarieren müsste.
Bei den Planeten erreichen wir durch den Einsatz der abstrakten Klasse hauptsächlich, dass wir Instanzen von Planet
verbieten und nur Instanzen von Gesteinsplanet
oder Gasplanet
erlauben.
Sonne erstellen
Die Sonne ist ein Stern. Erzeugen Sie einen value sonne
mit Radius 50. Die Sonne befindet sich im Zentrum unseres Sonnensystems, daher soll die Sonne auch in der Mitte unserer Anzeigefensters (des views) platziert werden. Dies gelingt mit:
x = Engine.view.width/2.0
y = Engine.view.height/2.0
// Hinweis: Ab Engine Version 0.16 engine.view.height klein schreiben, da es sich um kein Singleton mehr handelt, sondern um ein Objekt
Wir erstellen das Sonnen Objekt in der Main.kt
und rufen anschließend Engine.run()
auf. Das bedeutet, dass zum Zeitpunkt der Erstellung des Objektes noch garkeine Engine (und damit kein view) existiert. Demzufolge können wir die x und y Koordinaten nicht bei der Erstellung des Objektes auf die Center-Koordinaten des Views festlegen.
Überschreiben Sie deshalb die animate()
Funktion in der Klasse Sonne
mit dem setzen der Koordinaten.
Planeten erstellen
Wir erstellen die Planeten unseres Sonnensystems. Hierbei modellieren wir die Gasplaneten so, dass sie einen Rand bekommen, der in Farbe und Dicke die Gashülle darstellen soll. Gesteinsplaneten sind einfach komplett einfarbig gefüllte Kreise.
Erzeugen Sie also Objekte der Planeten in der Main.kt
. Um sie einfacher bei der Engine anzumelden, stecken wir sie vorher in ein sogenanntes Array.
In einem Array kann man viele Objekte des gleichen Typs (beachte: Auch des gleichen Supertyps!) sammeln.
Wir erstellen ein Array mit dem Befehl arrayOf()
:
val sonnenSystem = arrayOf(sonne, merkur, venus, erde, mars, jupiter, saturn, uranus, neptun)
// sonnenSystem hat den Datentyp Array<Kreis>
Platzierung und Größen der Planeten
Versuchen Sie, die wahren Größen der Planeten und natürlich deren Umlaufbahnen im Sonnensystem den realen Gegebenheiten anzupassen. Allerdings wird man einige Vereinfachungen machen müssen, um alle Planeten anzeigen lassen zu können. Ein Beispiel könnte so aussehen:
val jupiter = Gasplanet(Umlaufbahn(180),Colors.BEIGE, 20, 5, Colors.BROWN)
Einbezug der Umlaufbahn
Den Radius der Umlaufbahn (also den Abstand von der Sonne) wurde in der Klasse Umlaufbahn
gekapselt. Dieser Wert muss nun natürlich bei der Platzierung der Planeten mit einberechnet werden. Hierzu sollte die Funktion animate()
in der Klasse Planet
wie folgt überschrieben werden:
override suspend fun animate() {
super.animate()
val mittelPunktSonnenSystemHorizontal = Engine.view.width/2.0
val mittelPunktSonnenSystemVertikal = Engine.view.height/2.0
x = mittelPunktSonnenSystemHorizontal + umlaufbahn.radius.toDouble()
y = mittelPunktSonnenSystemVertikal
}
Das Sonnensystem könnte dann so aussehen:
Rotation der Planeten
Jetzt sollen die Planeten natürlich in Kreisbahnen um die Sonne rotieren. Lineare Bewegungen haben wir ja schon kennen gelernt. Hierzu musste man einfach die x- und y-Koordinaten erhöhen. Jetzt brauchen wir aber eine Möglichkeit, die x und y-Koordinaten so zu berechnen, dass sie eine Kreisbahn beschreiben. Bevor Sie sich das folgende Video ansehen, sollten Sie sich selbst kurz überlegen, ob Sie eine Idee haben, wie wir die x- und y-Koordinaten auf dieser Kreisbahn berechnen.
Geometrie am Kreis
Ergänzen der Klassen
Da wir nun wissen, dass wir sowohl einen Winkel, wie auch eine Umlaufgeschwindigkeit für die Animation benötigen, ergänzen wir die Klasse Umlaufbahn
um diese Größen:
class Umlaufbahn (var radius : Int = 100, var umlaufGeschwindigkeit : Double = 1.0, var winkel : Double = 0.0)
Jetzt können wir die x- und y-Koordinaten endlich berechnen. Ändern Sie dazu die animate()
Funktion in Planet so ab, dass sie die folgenden Berechnen erfüllt.
- Berechnung der x-Koordinate mit Hilfe des Cosinus
- Berechnung der y-Koordinate mit Hilfe des Sinus
- Addieren Sie
mittelPunktSonnenSystemHorizontal
undmittelPunktSonnenSystemVertikal
zu den zugehörigen Werten - Zum Ende der
animate()
Funktion muss noch der Winkel im umlaufbahn-Objekt um die Umlaufgeschwindigkeit erhöht werden.
Kotlin liefert Funktionen für Sinus und Cosinus Berechnungen mit. Um diese zu nutzen, müssen sie
kotlin.math.*
ganz oben in ihrerPlanet.kt
Datei importieren.import kotlin.math.*
Fine Tuning
Stellen Sie eine moderate Umlaufgeschwindigkeit ein (denken Sie an die Einheit Radiant -> Kleine Werte unter 1 für die Geschwindigkeit vergeben)
Aufgaben
- Geben Sie jedem Planete eine eigene Umlaufgeschwindigkeit
- Fügen Sie einen Hintergrund ein, der das Weltall und einzelne entfernte Sterne zeigt
- Ersetzen Sie die geometrischen Kreisfiguren der Planeten durch Bilder
- Um die Umlaufbahnen etwas realistischer aussehen zu lassen, können wir die Kreisbahnen in Ellipsen verwandeln. Modifizieren Sie hierzu die x- oder y-Koordinaten der Umlaufbahn (etwa durch 2 Teilen)
- Manche Planeten können auch Monde haben. Fügen Sie Monde ein, indem Sie einer Klasse
Mond
einePlanet
-Property geben, um den dieser kreist. DerMond
soll einfach eine Kreisbwegung um den (sich ständig ändernden) Mittelpunkt des Planeten machen.