Die vorliegenden Materialien wurden von Daniel Hoherz und André Tempel erstellt. Sollten andere Editoren die Materialien erstellt haben, werden diese explizit genannt.
Sie wissen bereits, was Klassen und Objekte sind, damit haben Sie die beste Voraussetzung den folgenden
Teil fast mühelos zu bewältigen.
Wir wollen uns nun der objektorientierten Programmierung mit der Programmiersprache Java widmen.
Es wird nötig sein, eine neue Sprache zu lernen. Dazu gehört es eben, dass besstimmte Vokabeln
und etwas Grammatik gelernt werden müssen. Da unterscheiden sich Programmiersprachen
von anderen gesprochenen Sprachen nicht. Allerdings ist die nun genutzte Programmiersprache
Java nur minimal anders, als Processing. Also sollten die meisten Dinge bekannt sein. Wir starten
trotzdem wieder ganz von vorne!
Öffnen Sie die Online-IDE in Ihrem Webbrowser und melden Sie sich mit den Login-Daten an.
Erstellen Sie einen neuen Workspace „ErsteSchritte“ und dann eine neue Datei mit dem gleichen Namen. Schreiben Sie dort den nebenstehenden Programmcode hinein.
Klicken Sie nun auf den grünen Pfeil am oberen Rand.
println("Hello Wolrd.");
- Bearbeiten Sie die folgenden Aufgaben in Einzelarbeit.
Suchen Sie im Quelltext den auf der Konsole angezeigten Text und verändern Sie ihn beliebig.
Sie sind beim Softwareunternehmen Hurricane als Spieleentwicklerin/Spieleentwickler angestellt.
Es soll ein klassischen Adventurespiel programmiert werden.
Die für die Story verantwortliche Abteilung, hat bereits eine kleine Beschreibung der Spielwelt verfasst:
In der magischen Welt von Eldoria, wo die Grenzen zwischen Traum und Wirklichkeit verschwimmen,
erstrecken sich endlose Landschaften, die von einer schimmernden Aura umgeben sind. Die
Luft ist erfüllt von einem süßen Duft nach blühenden Lichtharfen, deren leuchtende Blüten in den
Farben des Regenbogens erstrahlen. Die Sonne, ein riesiger, goldener Drache, schwebt am Himmel
und wirft ihr warmes Licht über die sanften Hügel und tiefen Wälder.
Eldoria ist eine Welt voller Wunder und Gefahren, in der Helden geboren werden und Monster lauern.
Die Geschichten von Mut, Freundschaft und Opferbereitschaft weben ein reichhaltiges Gewebe,
das die Herzen der Bewohner erfüllt und sie zu Taten inspiriert, die die Zeit überdauern. Hier, in
dieser fantastischen Welt, ist alles möglich – solange das Licht der Hoffnung leuchtet.
In unserem Spiel sollen alle Heldencharaktere die folgenden Aspekte beinhalten: Die Helden benötigen
- einen Namen,
- Lebenspunkte (hp),
- Manapunkte (mp), zum Anwenden verschiedener Fähigkeiten,
- einen Stärkewert, der für Angriffe und andere körperliche Aktivitäten relevant ist,
- einen Intelligenzwert, der für mentale Aktivitäten relevant ist,
- einen Rüstungswert, der für die Minderung von Schaden bei physischen Angriffen relevant ist.
public class Held {
private String namen;
private int hp;
private int mp;
private int staerke;
private int intelligenz;
private int ruestung;
public void changeHp(int b) {
if (hp+b < 0) {
hp = 0;
} else {
hp = hp + b;
}
}
public int getHp() {
return hp;
}
}
Vergleichen Sie den Quellcode mit der Abbildung und ordnen Sie die Komponenten einander zu.
Im obigen Beispiel ist der klassische Aufbau von
Java-Programmen zu erkennen. Java arbeitet
stets in Klassen, welche durch das Schlüsselwort
class definiert werden. Der Klassenname wird immer
groß geschrieben. Die Klasse hat noch das
Schlüsselwort public vorweg, welches anzeigt,
dass auch andere Klassen mit dieser interagieren,
also auf sie zugreifen, können.
Die Operation changehp(int b)
wird mit dem Schlüsselwort void gekennzeichnet,
da bei Ausführung der
Operation kein Wert entsteht, der zurückgegeben wird. Hinter dem Namen
jeder Operation steht ein Klammerpaar,
welches bei changehp(int b) den Inhalt int b hat, da hier bei Ausführung eine
Ganzzahlvariable erwartet wird, welche innerhalb der Operation zur Veränderung der Lebenspunkte
genutzt wird. Da der Computer alle Übergabeparametern nach der Ausführung einer Operation
wieder löscht, ist die Bezeichnung b außerhalb von changehp(int b) frei und kann anderweitig genutzt werden. Man könnte also bei einer anderen Operation, die eine int als Übergabe erhält, diese ebenfalls als b bezeichnen.
Mit der Operation gethp() soll ein Objekt der Klasse Held seine aktuellen hp zurückgeben.
Das Attribut hp der Klasse Held ist vom
Datentyp int, weshalb bei dieser Operation anstelle des Schlüsselwortes void vor dem Operationenname nun int stehen muss.
Das Schlüsselwort return muss in jeder Operation, die etwas zurückgibt, also ohne void, stehen.
Dahinter kommt das, was zurückgegeben werden soll. Hier der Wert der Variablen hp. Sobald
die return-Anweisung ausgeführt wurde, wird die Operation beendet. Somit müssen alle anderen
Anweisungen, welche innerhalb der Operation ausgeführt werden sollen, vor der return-Anweisung
stehen.
- Starten Sie den Java-Editor und implementieren Sie die ersten Klassen.
-
Es soll eine neue Klasse
Goblinimplementiert werden. Ein Goblin soll einen Namen und eine Farbe haben, Lebenspunkte, einen Rüstungswert, einen Wert für Schaden, den er verursacht und ein Attributaggressiv, welches angibt, ob er aggressiv ist oder nicht. Für letzteres informieren Sie sich über den Datentyp Wahrheitswert oder auchboolean.
Weiterhin soll ein Goblin seinen aktuellen Rüstungswert verändern und auch zurückgeben zu können, es soll Möglichkeiten geben, sich die aktuellen Lebenspunkte ausgeben zu lassen und die Lebenspunkte zu verändern und es soll die OperationengetAggro()undchangeAggro()geben, wobei die erste den aktuellen Wert des Attributsaggressivzurückgibt und die zweite den aktuellen Wert ändert. Ist der aktuelle Wert geradetrue, wird er auffalsegeändert und ist er geradefalse, wird er auftruegesetzt.
Hinweis: Für die Umsetzung ist die Kontrollstruktur "Verzweigung" bzw. if-else-Anweisung in Java notwendig. Diese wird wie in Processing genutzt.
Erstellen Sie in der Online-IDE die Klasse Held, wie weiter oben bereits abgebildet. Legen Sie dazu zunächst einen neuen Workspace "Adventure" und legen Sie eine Datei an mit dem Namen "Held".
Erweitern Sie die Klasse um die Operationen changeMp(int a), welche die aktuellen mp um den Wert a ändert und getMp(), welche die aktuellen mp zurückgibt.
Entwerfen Sie eine Klassenkarte der Klasse Goblin.
Implementieren Sie die Klasse Goblin in einer neuen Datei in der Online-IDE mit dem Namen "Goblin".
Hinweis: Jede Klasse wird in einer eigenen Datei angelegt.
Zusätzlich soll ein Goblin eine Operation kaempfen ausführen können. Wenn er aggressiv ist, wird sein Schadenswert als Zeichenkette ausgegeben. Wenn er nicht aggressiv ist, soll "Ich will nicht angreifen!" ausgegeben werden.
Erweitern Sie Ihre Klasse Goblin entsprechend.
Bis hier hin haben wir nur "Trockenübungen" gemacht. Es ist noch nichts passiert. Das liegt aber auch daran, dass wir dem Programm auch noch überhaupt nicht gesagt haben, dass es etwas machen soll. Das wollen wir nun ändern.
Zunächst benötigen wir eine Operation, welche ein konkretes Objekt einer Klasse erstellen, fachsprachlich nennt man dies instanziieren, kann. Diese Aufgabe übernimmt ein Konstruktor. Für unsere Klasse Held sieht das folgendermaßen aus.
Mit Hilfe dieses Konstruktor werden beim Instanziieren eines Objekts der Klasse Held die Werte n als Werte für das Attribut name, h für hp, m für mp, s für staerke, i für intelligenz und r für ruestung zugewiesen.
Es fällt wahrscheinlich auf, dass vor den Attributen plötzlich "private" steht. Das hat den Grund, damit andere Objekte nicht direkt auf die Attribute eines Helden zugreifen können. Die Sichtbarkeit der Attribute wird durch private so eingeschränkt, dass nur das Objekt selbst die Werte seiner Attribute sehen und ändern kann. Möchte man dies allen anderen Objekten, auch von Fremdklassen, erlauben, würde man "public" davor schreiben, wie bei den Operationen.
Held held1 = new Held("Cloud", 10, 5, 2);
Für das Instanziieren eines Helden erstellen wir eine neue Klasse, wir nennen sie bspw. Spiel, in welcher das Erzeugen geschehen soll.
Schauen wir uns zwei weitere Zeilen an.
held1.changeHp(10);
println(held1.getHp());
Die Klasse Spiel kann mit der Notation held1. auf alle Attribute und Operationen von held1 zugreifen, die nicht mit private gekennzeichnet sind. Nachdem man held1 deklariert hat und
held1. schreibt, öffnet sich ein Menü:
Das "Punkt-Menü" ist sehr hilfreich und erleichtert das Programmieren immens.
-
Bearbeiten Sie die folgenden Aufgabe in Einzelarbeit. Sie dürfen sich aber gerne gegenseitig unterstützen.
Wir bleiben zunächst nur bei der Klasse
Held.
Erweitern Sie zunächst die Klasse Held um einen Konstruktor, wie Sie es bereits oben in der Erklärung gesehen haben.
Erstellen Sie in Ihrem Workspace eine neue Datei mit dem Namen Spiel. In dieser Datei werden wir das Spiel ausführen.
Initilisieren Sie in der Datei Spiel ein Objekt der Klasse Held mit selbst gewählten Attributswerten. Wie das geht, steht auch nochmal in der Erklärung oben.
Führen Sie auf Ihrem Objekt der Klasse Held einige Operationen aus und lassen Sie sich auch mit Hilfe von Ausgaben verschieden Wertebelegungen der Attribute ausgeben. Hinweis: Wenn Ihre Attribute alle private sind benötigen Sie dafür auch get-Operationen und den Befehl print() oder println().
- Nun kümmern wir uns um den Goblin.
Erweitern Sie Ihre Klasse Goblin um einen geeigneten Konstruktor, ähnlich wie der zur Klasse Held.
Instanziieren Sie in Ihrer Klasse Spiel ein Objekt der Klasse Goblin.
Erweitern Sie Ihre Klassen Held und Goblin um die Operation getName(), welche den Namen zurückgibt und testen Sie Ihre Operationen in der Klasse Spiel.
Führen Sie auf Ihrem Objekt der Klasse Goblin einige Operationen aus und lassen Sie sich auch mit Hilfe von Ausgaben verschieden Wertebelegungen der Attribute ausgeben. Hinweis: Wenn Ihre Attribute alle private sind benötigen Sie dafür auch get-Operationen und den Befehl print() oder println().
- Im Folgenden werden wir unser Szenario erweitern.
- Bei der jeweiligen Ausführung von
angreifensoll der verursachte Schaden in Höhe des jeweiligen Stärkewertes die aktuellen Lebenspunkte verringern. - Der Rüstungswert wird vom Schaden abgezogen.
Erweitern Sie zunächst die Klassen Held und Goblin um weitere get- Operationen, welche den Namen zurückgibt und set-Operationen, um die Werte zu verändern. Hinweis: zurückgeben bedeutet, dass eine Operation durch eine return-Anweisung einen Wert zurückgibt. ausgeben bedeutet, dass ein Wert auf der Konsole angezeigt werden soll, also etwas mir print().
Die Klasse Held wird nun um eine Operation angriffsschrei erweitert. Die Operation erhält die aktuellen hp des Helden als Übergabeparameter und soll auf der Konsole einen Angriffsschrei des Helden drucken.
Sind die Lebenspunkte des Helden mindestens 80, soll er einen mächtigen Angriffsschrei "Ahaaaaahaaaaahaaa" ausführen. Wenn er weniger als 80 und mindestens 40 Lebenspunkte hat, wird nur noch ein normaler Angriffsschrei "Ahaaaaha" produziert. Bei noch weniger, aber mehr als 0 Lebenspunkten, ist der Angriffsschrei nur noch schwach: "Aaa" und bei 0 oder weniger Lebenspunkten, werden drei Punkte auf der Konsole ausgegeben.
Hinweise: Hierfür sind geschachtelte Verzweigungen (if-else-Anweisungen) oder korrekt hintereinander
gesetzte bedingte Anweisungen (if-Anweisungen) notwendig.
Implementieren und testen Sie die Operationen in der Klasse Spiel.
Die Klassen Held und Goblin sollen um eine Operation zeigeWerte erweitert werden. Die
Operation soll die Werte aller Attribute auf der Konsole ausgeben.
Gestalten Sie die Ausgabe so, dass man auch weiß, was angezeigt wird.
Testen Sie die Operationen in der Klasse Spiel.
Die Operationen angreifen(Held h) und angreifen(Goblin g) sollen in jeweils passend beiden Klassen eingefügt werden. Es gibt jetzt eine Übergabeparameter vom jeweils anderen Datentyp (in Java sind Klassen Datentypen, wir haben also Daten vom Datentypen Held und Goblin).
Der Operation angreifen der Klasse Held wird also ein Objekt der Klasse Goblin übergeben bekommen und der Operation angreifen der Klasse Goblin wird ein Objekt der Klasse Held übergeben bekommen.
Bei der Operation angreifen sollten Sie eine Verzweigung (if-else) nutzen.
Ein Goblin muss aggressiv sein, um anzugreifen und generell sollte der Schaden eines Goblin bzw. die Stärke eines Helden größer als der Rüstungswert des Gegners sein. Ansonsten gibt es keinen Schaden.
Testen Sie die neuen Operationen in Ihrer Klasse Spiel.
Ergänzen Sie beide Klassen Held und Goblin um die Operation tot(). Diese Operation soll true zurückgeben, wenn die Lebenspunkte 0 oder weniger sind und sonst false.
Implementieren und testen Sie Ihre Operation.
Schauen Sie sich den YouTube-Short an. Wieso muss der Spieler in dem Spiel häufiger würfeln? Warum werden solche Mechaniken eingeführt? Und warum haben wir das noch nicht eingeführt?
Für Zufallswerte gibt es in Java, ähnlich wie in Processing, eine Operation. In Processing haben wir mit random eine Zufallszahlen erzeugen können. In Java wird dieses mit der Operation Math.random() gemacht. Dabei liefert Math.random() eine zufällige Gleitkommazahl aus dem Intervall [0, 1[, also zwischen 0 und 1, wobei die 0 mit drin ist, aber die 1 nicht mehr, also sind alle zufällig erzeugen Gleitkommazahlen echt kleiner als 1.
Wie Sie der Grafik entnehmen können, kann mit dem Wert der Operation gerechnet werden, um bestimmte Anforderungen zu erfüllen. So sorgt eine Multiplikation (blau) dafür, dass das Intervall vergrößert wird. Eine Addition (grün) sorgt für eine Verschiebung des Intervalls. Den letzten schauen wir uns etwas genauer an. (int) (Math.random()*6+1) erzeugt mit dem vorangestellten Datentyp eine Typumwandlung zu eben diesem. Also wird aus der Gleitkommazahl, welche Math.random() liefert, eine Ganzzahl gemacht und dabei werden einfach die Nachkommastellen "abgeschnitten". Also wird aus 5.99 dann einfach 5. Typumwandlungen funktionieren zwischen primitiven Datentypen (Ausnahme boolean).
Nun gibt es in Java, wie in jeder anderen Programmiersprache auch, Kontrollstrukturen. Daher ist es nicht verwunderlich, dass wir auch hier bedingte Schleifen gibt. In Java sieht der Befehl für eine bedingte Schleife wie nebenstehend aus:
while (Bedingung) {
Anweisung(en)
}
Es gilt, dass solange die Bedingung erfüllt ist, dass die Anweisungen innerhalb der geschweiften Klammern ausgeführt werden. Das kennen wir aus Processing auch so.
-
Wir nutzen weiterhin unsere Klassen
Held,GoblinundSpielund erweitern diese um weitere Funktionen mit Zufallswerten.
Wir verändern die Operation angreifen() des Heldens. Es soll nun nicht mehr ein fester beim Angriff als Schaden an den Goblin ausgerichtet werden, sondern durch eine zufällige Komponente erweitert werden. Ein gewisser Grundschaden soll erhalten bleiben, aber daneben soll noch eine zufällige Komponente beim angerichteten Schaden mit einbezogen werden.
Erweitern Sie die Operation und testen Sie diese.
Bei Angriffen gibt es auch die Chance, dass man verfehlt. Erweitern Sie die Operationen angreifen() der Klassen Held und Goblin, um die Möglichkeit, dass ein Angriff auch verfehlen kann und somit keinen Schaden verursacht.
Kritische Treffer sind selten, aber besonders stark. Ein Angriff kann auch kritisch sein und verursacht doppelten Schaden, passiert aber nur in sehr seltenen Fällen.
Erweitern Sie die Operation angreifen(), sodass auch kritische Treffer geschehen.
- Nun soll ein Spielablauf simuliert werden, dazu nutzen wir nun bedingte Schleifen.
-
Fügen Sie eine Operation
riskanterAngriff(Goblin g)hinzu, welche stärker als der normale Angriff ist, aber wahrscheinlicher fehlschlägt. -
Fügen Sie eine Operation
heilen(int h)hinzu, welchempverbraucht umhpwiederherzustellen.
Entwickeln Sie mit Hilfe einer while-Schleife einen Algorithmus in Ihrem Spiel, in dem der Held den Goblin solange angreift, bis dessen Lebenspunkte auf 0 oder weniger reduziert sind, er also tot ist und natürlich soll der Held bei jedem Angriff seinen Angriffsschrei loslassen!
Hinweis: Eine Textausgabe nach jedem Angriff ist sinnvoll, um den Verlauf des Kampfes zu verfolgen. Außerdem ergibt es Sinn, die Spiel-Datei ein bisschen "aufzuräumen", bevor die Ausgaben unübersichtlich werden.
Nun soll der Goblin sich auch wehren.
Erweitern Sie die Klasse Spiel so, dass Held und Goblin sich abwechselnd angreifen und das Spiel endet, wenn einer der beiden tot ist. Fügen Sie weiterhin sinnvolle Textausgaben hinzu.
Hinweis: Wenn zwei Bedingungen gleichzeitig abgefragt werden sollen, kann das mit (bedingung1 && bedingung2) umgesetzt werden.
Der Goblin soll nun eine zufällige Belohnung geben können, wenn er besiegt ist.
Erstellen Sie dafür eine Operation reward(Held h) welche zufällig auswählt, welches Attribut des Helden um wieviel verbessert wird. Welche Attribute zur Auswahl stehen und welche Wertebereiche dafür jeweils Sinn ergeben, können Sie selber entscheiden. Ggf. müssen Operationen in Held ergänzt werden, um weitere Attribute des Helden verändern zu können.
Nun wollen wir etwas Interaktion hinzufügen. Zunächst erweitern wir dafür den Helden um eine alternative Aktion zum Angriff.
Damit Spielerinnen und Spieler eine Fähigkeit auswählen können, brauchen wir einen Input/Eingabe. Den können wir mit den folgenden Befehlen für die verschiedenen Datentypen holen, wichtig ist nur, dass die eingegebenen Werte in entsprechende Variablen gespeichert werden, damit wir damit arbeiten können:
String eingabeText = Input.readString("Bitte eine Zeichenkette eingeben: ");
int eingabeGanzzahl = Input.readInt("Bitte eine Ganzzahl eingeben: ");
double eingabeGleitkommazahl = Input.readDouble("Bitte eine Gleitkommazahl eingeben: ");
float eingabeGleitkommazahl2 = Input.readFloat("Bitte eine Gleitkommazahl eingeben: ");
boolean eingabeWahrheitswert = Input.readBoolean("Bitte einen Wahrheitswert eingeben: ");
Erweitern Sie die Klasse Spiel so, dass bei jedem Zug des Helden nach einer Eingabe gefragt wird, welche Fähigkeit verwendet werden soll. Eine Textausgabe sollte vorher den User dazu auffordern, eine sinnvolle Eingabe zu machen. (z. B: "Gebe ein: a für angreifen oder h für heilen oder r für riskanter Angriff")
Hinweis: Bei String Variablen (hier bspw. eingabeText) müssen Sie diese mit der Operation eingabeText.equals("hier String("hallo") vergleichen. Diese gibt als boolean zurück, ob eingabeText gleich dem String "hallo" ist. Bei den primitiven Datentypen genügt == zum Vergleich.
(anspruchvoller) Wir müssen immer vom DAU (Dümmsten Anzunehmenden User) ausgehen, der auch Dinge macht, die nicht vorgesehen sind. Lassen Sie die Aufforderune so lange wiederholen, bis eine gültige Eingabe vorliegt.
-
Die folgenden Aufgaben wiederholt alle Inhalte an einem anderen Kontext.
Stellen Sie sich vor, dass Sie eine App entwickeln, die Ihnen hilft, ihre schulischen und privaten Aufgaben zu organisieren. Jeder Aufgabe/ToDo hat einen Titel, eine Priorität und einen Status (erledigt/offen). getTitel()gibt den Wert des Attributstitelvom Datentyp Zeichenkette zurückgetPrioritaet()gibt den Wert des Attributsprioritaetvom Datentyp Ganzzahl zurückgetErledigt()gibt den Wert des Attributserledigtvom Datentyp Wahrheitswert zurücksetTitel(String neuerTitel)gibt dem Attributtitelden Wert aus dem ÜbergabeparameterneuerTitelvom Datentyp ZeichenkettesetPrioritaet(int neuerPrio)gibt dem Attributprioritaetden Wert aus dem ÜbergabeparameterneuerPriovom Datentyp GanzzahlsetErledigt(boolean status)gibt dem Attributtitelden Wert aus dem Übergabeparameterstatusvom Datentyp Wahrheitswert
Implementieren Sie als erstes die Klasse ToDo mit den Attributen titel vom Datentyp Zeichenkette, prioritaet vom Datentyp Ganzzahl und erledigt vom Datentyp Wahrheitswert.
Erstellen Sie einen Konstruktor der für die Klasse ToDo, der dafür sorgt, dass titel und prioritaet übergeben werden müssen und das Attribut erledigt wird standardmäßig auf false gesetzt.
Erstellen Sie eine weitere Klasse Aufgabenverwaltung, in der Sie eine ToDo anlegen, also ein Objekt der Klasse ToDo erzeugen und ihr Werte zuweisen.
Wir gehen zurück zur Klasse ToDo. Implementieren Sie für jedes Attribut eine get- und eine set-Operation.
Wir gehen zurück zur Klasse ToDo. Implementieren Sie für jedes Attribut eine get- und eine set-Operation:
Wir gehen nun in die Klasse Aufgabenverwaltung. Testen Sie hier geeignet die Funktion Ihre in Aufgabenteil e implmentierten Operationen.
Wir gehen zurück in die Klasse ToDo
Wir verändern die Operation setErledigt(). Wenn der Wert vom Attribut erledigt auf true gesetzt wird, soll eine motivierender Spruch, wie bspw. "Super gemacht! Jetzt gibt’s eine Pause!" auf der Konsole ausgegeben werden.
Eine Variation: Nur wenn zuvor der Wert des Attributs erledigt auf false war und nun zu true geändert wird, wird der motivierende Spruch ausgegeben.
Gehen Sie nun zur Klasse Aufgabenverwaltung und testen Sie Ihre Veränderungen aus Aufgabenteil g.
Gehen Sie nun zur Klasse ToDo.
Die Operation getPrioToSternchen() soll eine Zeichenkette zurück geben. Die Zeichenkette soll nur aus Sternchen bestehen ("*"). Der Zahlenwert vom Attribut prioritaet wird in Sternchen umgewandelt (bspw. "***" für eine Priorität von 3).
Implementieren Sie die Operationen in der Klasse ToDo und testen Sie sie in der Klasse Aufgabenverwaltung Ihre Operation.
In der Klasse ToDo soll nun die Operation ausgabe() erstellt werden, die eine Ausgabe der ToDo auf der Konsole erzeugt. Die Ausgabe soll am Ende so aussehen.
[ ] Vokabeln lernen (4) – Offen.
[X] Zimmer aufräumen (2) – Erledigt!
Eine alternative Variante macht aus den Zahlen für die Priorität die Sternchen:
[ ] Vokabeln lernen (****) – Offen.
[X] Zimmer aufräumen (**) – Erledigt!
Implementieren Sie die Operation ausgabe in der Klasse ToDo und testen Sie diese in der Klasse Aufgabenverwaltung.
public class Magier extends Held {
private int mp;
public Magier(String n, int h, int m, int s, int i, int r){
super(n, h, s, i, r);
this.mp = m;
}
...
}
Im linken Bild sehen Sie die Vererbung der Oberklasse Held an die beiden Unterklassen Magier und Krieger. Wichtig ist hier nun, dass der Held keine Manapunkte mehr hat, sondern nur der Magier. Rechts daneben sehen Sie die Umsetzung der Vererbung in Java am Beispiel der Unterklasse Magier.
Stellen Sie Vermutungen darüber an, was die Begriffe extends (Zeile 1) und super(...); (Zeile 5) machen.
Vererbungen werden in Java mit dem Schlüsselwort extends kenntlich gemacht. Dabei muss links vom extends die Unterklasse und rechts die Oberklasse stehen. In unserem Beispiel wird das gelesen als, dass Magier vom Held erbt.
Ein weiterer wichtiger Punkt bei der Vererbung ist, dass im Konstruktor der Unterklasse alle Attribute erhoben werden, wie zuvor auch, aber das alle Attribute, die zur Oberklasse gehören, an diese mit dem Befehl super() weitergegeben werden müssen. Der Konstruktor der Oberklasse sorgt dann dafür, dass die Attributswerte gesetzt werden. Nur Attribute, die es ausschließlich in der Subklasse gibt, müssen selbst gesetzt werden (im Beispiel die Manapunkte).
-
Wir verändern zuerst die Klasse
Heldund ergänzen die KlasseMagierals Subklasse.
Erstellen Sie eine Klasse Magier, wie im obrigen Beispiel gezeigt, aber zunächst ohne die Operation zaubern.
Entfernen Sie aus der Klasse Held alles, was mit Manapunkten zu tun hat und ergänzen Sie es, falls noch nicht geschehen, in der Klasse Magier und testen Sie Ihre Implementierung, indem Sie ein Objekt der Klasse Magier erzeugen.
Die Operation zaubern(Goblin g) soll einen Goblin mit einem Feuerball angreifen, dabei bestimmt sich der Schaden in Höhe der Manapunkte und der Intelligenz. Die Manapunkte werden dabei verbraucht und auch muss vor dem Wirken natürlich geprüft werden, ob überhaupt noch Manapunkte vorhanden sind, wenn keine vorhanden sind, kann nicht gezaubert werden. Rüstung schützt nicht vor Magie.
Entwickeln Sie die Operation zaubern und testen Sie diese geeignet.
Entwickeln Sie eine weitere Operation besaenftigen(Goblin g), welche einen Goblin nicht mehr aggressiv macht.
(Variante) Sie können auch dafür sorgen, dass der Zauber nur mit einer bestimmten Wahrscheinlichkeit gelingt.
Der Magier ist ein eher schwächlicher Typ und macht bei seinen Angriffen weniger schaden. Nämlich nur 50% des ursprunglichen Schadens.
Implementieren Sie in der Klasse Magier die Operation angreifen(Goblin g), welche eben nur 50% des Schadens macht.
Hinweis: Gibt es eine Operation sowohl in der Super- als auch in der Subklasse, die identisch sind, dann überschreibt die Subklasse damit die Operation der Superklasse und dieses nennt man überschreiben.
-
Jetzt ergänzen wir noch die Klasse
Krieger.
Erstellen Sie eine Klasse Krieger, wie im obrigen Beispiel gezeigt, aber zunächst ohne die Operation doppelAngriff().
Die Operation doppelAngriff() soll zwei Angriffe nacheinander ausführen, aber benötigt dafür immer 2 Ausdauerpunkte. Wenn nicht ausreichend Ausdauer vorhanden ist, kann der Angriff nicht ausgeführt werden.
Implementieren Sie die Operation doppelAngriff entsprechend.
Testen Sie Ihre Implementierung.
-
Die Klasse
Goblinsoll nun zur Unterklasse der KlasseMonsterwerden, und nur die KlasseGoblinist aggressiv oder nicht.
Implementieren Sie eine Klasse Monster mit den Attributen und Operationen der Klasse Goblin.
Verändern Sie die Klasse Goblin, sodass sie von der Klasse Monster erben kann und nur das Attribut aggressiv es von der Klasse Monster abgrenzt.
Sorgen Sie bei den Klassen Held, Magier und Krieger dafür, dass nicht mehr nur Goblins angegriffen werden können, sondern Monster.
Goblins haben die nervige Eigenschaft, dass sie auch ihresgleichen und andere Monster angreifen.
Implementieren Sie in der Klasse Goblin die Operation angreifen(Monster m). Wir haben nun zwei Mal die Operation angreifen nur mit unerschiedlichen Übergabeparametern. Dieses nennt man überladen.
Testen Sie Ihre Implementierung geeignet.
For-Schleifen kennen Sie bereits auch aus Processing. Daher betrachten Sie einmal den folgenden Quellcode und notieren Sie, welche Ausgabe jeweils erzeugt wird.
| Code | Ausgabe |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- Im Folgenden werden wir die for-Schleife verwenden.
- eine while-Schleife in eine for-Schleife umwandelt.
- eine for-Schleife in eine while-Schleife umwandelt.
Erstellen Sie in der Klasse Spiel unter Nutzung von while-, for-Schleifen und bedingten Anweisungen eine kleine Abfolge von Abfragen auf der Konsole, bei denen ein Nutzer eine Aktion für einen Magier oder Krieger (oder Held gA) auswählen kann. Nachdem diese ausgeführt wurde, soll gefragt werden, ob noch etwas getan werden soll und abhängig von der Antwort geschieht noch etwas.
Vergleichen Sie die beiden folgenden Schleifen miteinander und formulieren Sie eine Beschreibung, wie man...
|
|
-
Der Held "Lenk" trifft eine Heldin im Spiel. Man weiß nicht, ob es seine Jugendliebe "Zolda" ist.
Im Folgenden sehen Sie ein Struktogramm einer OperationprüfeName(Held h).
Struktogramm der Operation prüfeName(Held h)
Analysieren Sie für die Helden mit den Namen "zolta" und "zolda" den Algorithmus. Nutzen Sie dazu auch die unten stehenden Tracetabellen.
| i | zeichen i von s | zeichen i von t | gefunden |
|---|---|---|---|
| i | zeichen i von s | zeichen i von t | gefunden |
|---|---|---|---|
Verändern Sie das Struktogramm so, dass der Algorithmus wie gewünscht arbeitet. Sie können hiermit eine Strukrtogramm digital erstellen.
- (eA - optional) Erweitern Sie Ihre Klassen um die folgenden Aspekte. Bachten Sie bitte, dass Sie nicht alles der Reihenfolge nach implementieren müssen.
-
Ergänze die Klasse
Kriegerum eine neue Operationverteidigen(), die den Rüstungswert des Helden um einen zufälligen Wert zwischen 1 und 3 erhöht. (optional) Verteidigen verbraucht 1 Ausdauer und kann auch nur genutzt werden, wenn Ausdauer vorhanden ist. -
Ergänze die Klasse
Kriegerum eine neue Operation doppelangriff(Goblin g), die den Helden zweimal hintereinander angreifen lässt, wobei der zweite Angriff nur 50% des normalen Schadens verursacht. (optional) Auch dieser Angriff verbraucht Ausdauer. -
Ergänze die Klasse
Kriegerum eine neue Operationkampfrausch(), die die Stärke des Helden für die nächsten drei Runden um 50% erhöht, aber danach seine Lebenspunkte um 10% reduziert. - Ergänze die Klasse
Goblinum eine neue Operationverstecken(), die die Wahrscheinlichkeit eines Angriffs auf den Goblin um 50% reduziert. Dies kann durch eine zufällige Entscheidung in der Methodeangreifen(Goblin g)des Helden berücksichtigt werden. Hinweis: ggf. Überladen -
Ergänze die Klasse
Goblin um eine neue Operationhinterhalt(), die den Schaden des Goblins im nächsten Angriff verdoppelt. -
Ergänze die Klasse
Goblinum eine neue Operationgiftangriff(Held h), die den Helden vergiftet und ihm für die nächsten drei Runden kontinuierlich Schaden zufügt. -
Ergänze die Klasse
Goblinum eine neue Operationschattenklinge(Held h), die den Helden angreift und dabei eine Chance von 20% hat, den Helden für eine Runde zu lähmen. -
Ergänze die Klasse
Magierum eine neue Operationbeschwoeren(), die einen neuen Begleiter (z.B. einen kleinen Drachen) beschwört. Der Begleiter hat eigene Attribute wie Lebenspunkte, Stärke und Rüstung und kann eigenständig angreifen. -
Implementiere eine Methode
feuerball(Monster m), die das Monster mit einem Feuerball angreift. Der Schaden des Feuerballs wird durch eine komplexe Berechnung basierend auf den Manapunkten und der Intelligenz des Magiers bestimmt und etwas Glück ist auch dabei. -
Implementiere eine Methode
heilung(Held h), die die Lebenspunkte eines anderen Helden um einen zufälligen Wert zwischen 5 und 15 erhöht. -
Ergänze die Klasse
Magierum eine neue Operationblitzschlag(Monster m), die das Monster mit einem Blitzschlag angreift und dabei eine Chance von 30% hat, den Goblin für eine Runde zu betäuben.

