MMProg: Praktikum: WiSe 2017/18: Pong01: Unterschied zwischen den Versionen
Kowa (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
Kowa (Diskussion | Beiträge) Keine Bearbeitungszusammenfassung |
||
Zeile 177: | Zeile 177: | ||
Insgesamt sind bei jeder Neuberechnung der Modellwelt fünf Fälle zu überprüfen: | Insgesamt sind bei jeder Neuberechnung der Modellwelt fünf Fälle zu überprüfen: | ||
* Kollision vom Ball mit der Bühne | |||
* Kollision von Schläger1 mit der Bühne | * Kollision von Schläger1 mit der Bühne | ||
* Kollision von Schläger2 mit der Bühne | * Kollision von Schläger2 mit der Bühne | ||
* Kollision von Schläger1 mit dem Ball | * Kollision von Schläger1 mit dem Ball | ||
* Kollision von Schläger2 mit dem Ball | * Kollision von Schläger2 mit dem Ball | ||
Legen Sie zunächst drei Dateien an: | Legen Sie zunächst drei Dateien an: | ||
* <code>model/CollisionStageBall.js</code> | |||
* <code>model/CollisionStagePaddle.js</code> | * <code>model/CollisionStagePaddle.js</code> | ||
* <code>model/CollisionBallPaddle.js</code> | * <code>model/CollisionBallPaddle.js</code> | ||
Fügen Sie jeweils eine Funktion (mit leerem Rumpf ein), die Sie exportieren: | Fügen Sie jeweils eine Funktion (mit leerem Rumpf ein), die Sie exportieren: | ||
* <code>collision(p_stage, p_ball)</code> | |||
* <code>collision(p_stage, p_paddle)</code> | * <code>collision(p_stage, p_paddle)</code> | ||
* <code>collision(p_ball, p_paddle)</code> | * <code>collision(p_ball, p_paddle)</code> | ||
Importieren Sie diese Funktionen in die Datei <code>model/ModelUpdate.js</code> | Importieren Sie diese Funktionen in die Datei <code>model/ModelUpdate.js</code> | ||
Zeile 222: | Zeile 222: | ||
''sollten Sie daher aus der Datei <code>modelUpdate.js</code> löschen.'' | ''sollten Sie daher aus der Datei <code>modelUpdate.js</code> löschen.'' | ||
Implementieren Sie nun die drei Methoden. | |||
'''Kollision vom Ball mit der Bühne''' | |||
Das ist zunächst ganz einfach. Kopieren Sie den Code aus <code>wk/model/collisionBB.js</code> und entfernen Sie die Tests | |||
für den linken und den rechten Bühnenrand. Später fügen Sie noch Code ein, der die Logik-Komponente informiert, wenn der Ball die | |||
Bühne verlässt. | |||
Wenn Sie das Programm mehrfach starten, sollte jetzt der Ball vom oberen und unteren Rand abprallen, aber rechts und links von der Bühne verschwinden. | |||
'''Kollision vom Schläger mit der Bühne''' | |||
Dieser Test ist noch einfacher. Wenn der obere Rand des Schlägers kleiner oder gleich dem oberen Rand der Bühne ist, | |||
wird der Schläger auf die Bühne zurück geschoben (<code>p_paddle.top = p_stage.top;</code>) und angehalten <code>p_paddle.stop();</code>. | |||
Die Kollissionserkennung mit dem unteren Rand erfolgt auf die gleiche Art und Weise. | |||
'''Kollision vom Ball mit dem Schläger''' | |||
Das ist eine sehr aufwändige Operation, da 8 Fäclle unterschieden werden müssten: Kollision | |||
des Balls mit einer der vier Seiten oder einer der vier Ecken. Folgender recht grober Test | |||
muss daher vorerst reichen (der Ball verhält sich allerdings ziemlich eigenartig, wenn | |||
er mit einer der Ecke oder Schmalseite des Schlägers kollidiert); | |||
<source lang ="javascript"> | |||
if (p_ball.y + 0.5*p_ball.r >= p_paddle.top && | |||
p_ball.y - 0.5*p_ball.r <= p_paddle.btm | |||
) | |||
{ | |||
if (p_ball.vx > 0 && // The ball is moving from left to right. | |||
p_ball.rgt >= p_paddle.lft && p_ball.lft < p_paddle.rgt | |||
) | |||
{ p_ball.rgt = p_paddle.lft; | |||
p_ball.vx = -p_ball.vx; | |||
} | |||
if (p_ball.vx < 0 && // The ball is moving from right to left. | |||
p_ball.lft <= p_paddle.rgt && p_ball.rgt > p_paddle.lft | |||
) | |||
{ p_ball.lft = p_paddle.rgt; | |||
p_ball.vx = -p_ball.vx; | |||
} | |||
} | |||
</source> | |||
Testen Sie Ihre Kollissionsfunktionen, indem Sie in der Konfigurationsdatei unterschiedliche | |||
Parameter eingeben: Startposition, Startgeschwindigkeit, Größe des Schlägers, evtl. auch unterrschiedliche Geschwindigkeiten des Balls. | |||
===FORTSETZUNG FOLGT=== | |||
==Quellen== | ==Quellen== | ||
<references/> | <references/> |
Version vom 7. Dezember 2017, 21:13 Uhr
Dieser Artikel erfüllt die GlossarWiki-Qualitätsanforderungen nur teilweise:
Korrektheit: 3 (zu größeren Teilen überprüft) |
Umfang: 4 (unwichtige Fakten fehlen) |
Quellenangaben: 3 (wichtige Quellen vorhanden) |
Quellenarten: 5 (ausgezeichnet) |
Konformität: 3 (gut) |
Inhalt | Game Loop 01 | Ball 02 | Ball 03 | Ball 03b | Pong 01
Musterlösung: SVN-Repository
Ziel
Ziel dieser Aufgabe ist es, eine einfach Variante des Spieleklassikers Pong zu implementieren.
Aufgaben
Laden Sie das leere Projekt WK_Pong01_empty auf Ihren Rechner.
Installieren Sie aber nicht die Node.js-Module, das machen Sie später. Sie finden das leere Projekt im
Repository-Pfad https://glossar.hs-augsburg.de/beispiel/tutorium/es6
im Unterordner empty
.
Erstellen Sie ein neues Projekt pong01
und kopieren Sie die Ordner src
und web
(samt Inhalt)
sowie alle Dateien, die Sie im Wurzelverzeichnis des Projektes WK_Pong01_Empty
finden, mittels Crtl-/Apfel-C
Crtl-/Apfel-V
in Ihr eigenes Projekt. (Die Frage, ob WebStorm seinen eigenen File Watcher zum
Übersetzen von ES6-Code in ES5-Code verwenden soll, beantworten Sie bitte mit „No“. Das erledigt Webpack für Sie.)
Sie können Ihr Projekt zur Übung auch im Subversion-Repository speichern. Das ist aber nicht so wichtig.
Nun können Sie in Ihrem eigenen Projekt die benötigten Node.js-Module installieren: npm i
.
In Ihrem Projekt finden Sie zwei Web-Anwendungen: index00.html
und index01.html
,
die wie üblich eine zugehörige JavaScript-App einbinden. Beide Apps sind identisch aufgebaut. Es handelt sich
um eine leicht modifizierte Variante der Musterlösung 6
von [[Praktikumsaufgabe Ball03b. Die „Moorhühner“ können per Mausklick
„abgeschlossen“ werden.
Um Aufgabe 1 zu lösen, sollten Sie game01
schrittweise abändern. Überprüfen Sie nach jedem Teilschritt,
ob die Anwendung noch läuft.
Aufgabe 1
Zur Implementierung von Pong können Sie auf Module aus dem Praktikum zurückgreifen.
Im obigen Diagramm sind nur einige Module farbig eingezeichnet. Die grauen Module können
Sie direkt aus der wk
-Library einbinden, die Sie unter src/js/lib/wk
finden.
Der zugehörige Import-Befehl lautet jeweils
import ... from 'wk/...';
Mehrere Beispiele finden Sie in der Datei game01/game.js
.
Um die WebStorm-Fehlermeldungen zu vermeiden, die angeben, dass ein wk
-Modul
nicht gefunden werden kann, sollten Sie Folgendes machen:
- Rechtsklick auf
src/js/lib/wk
→Mark Directory as
→Resource Root
(Falls die wk
-Modul dann immer noch als fehlerhaft markiert werden,
sollten Sie WebStorm neu starten.)
Die im obigen Diagramm farbig markierten Module müssen Sie entweder erstellen oder – wenn sie schon existieren – an Ihre Gegebenheiten anpassen.
Vögel durch einen Ball ersetzen
Als erstes sollten Sie die Zahl der Vögel reduzieren, indem Sie einfach in der Konfigurationsdatei json/config01.json
den Counter @count
in model.bird
entfernen. Außerdem sollten Sie den String
"birds"
durch "ball" ersetzen und die Array-Klammer [ ]
um das Huhn-Model-Konfigurations-Objekt entfernen.
Letzteres ist wichtig, da die Logik direkt auf das Ball-Objekt zugreifen wird, sobald es die Bühne verlässt,
um eine neue Runde starten zu können. Ein Zugriff auf Array-Objekte wird vom Game-Modul (noch) nicht unterstützt.
(Funktioniert das Spiel noch? Es sollte nur noch ein Huhn herumflattern.)
Ersetzen Sie jetzt das animierte Huhn durch einen farbigen Kreis.
Ein Beispiel für eine zugehörige View-Konfiguration finden Sie in der Konfigurations-Datei
Musterlösung Ball03 (04a): view.orange
oder view.blue
.
Ersetzen Sie in Ihrer Konfigurationsdatei view.hen
durch eine der beiden Graphics-Konfigurationen
und ersetzen Sie den View-Bezeichner "hen"
unter model.ball.view
durch
den von Ihnen gewählten View-Bezeichner.
Wenn Sie jetzt das Programm laufen lassen, fliegt kein Ball mehr über die Bühne. In der Browser-Konsole wird folgender Fehler angezeigt:
v_json_map[c_config_view.class] is not a constructor
Das liegt daran, dass Sie in der Konfigurations-Datei angegeben haben,
dass die Klasse ViewGraphicsCircle
(aus wk/view/ViewGraphicsCircle.js
) zum Rendern der
View verwendet werden soll. Das Huhn wurde dagegen mit ViewAnimatedBird
gerendert.
Das Game-Modul game/game01.js
kann grundsätzlich jede beliebige Klasse zum Rendern verwenden.
Allerdings muss es diese importieren und unter dem Namen, der in der Konfigurationsdatei verwendet wird
in der Hashmap v_json_map
eingefügt werden.
Verbessern Sie game/game01.js
entsprechend.
Wenn alles funktioniert können Sie den Import der Klasse ViewAnimatedBird
aus
game/game01.js
entfernen (v_json_map
nicht vergessen). Die Datei
view/ViewAnimatedBird.js
können Sie löschen. Sie wird nicht mehr benötigt.
Feste Bühnengröße
Definieren Sie in der Konfigurationsdatei eine feste Bühnengröße (z. B. Breite: 600 Pixel, Höhe: 400 Pixel).
Das Hintergrundbild ist nicht sehr passend für Pong. Wenn Sie ein besseres haben, ersetzen Sie es,
ansonsten löschen Sie in der Konfigurationsdatei einfach das Attribut model.stage.view
.
Legen Sie die Startposition des Balls in die Mitte der Bühne und sorgen Sie dafür, dass er einen festen Radius hat. Die Geschwindigkeit sollte weiterhin zufällig gewählt werden. Allerdings werden Sie später vermutlich den Geschwindigkeitsbereich anpassen.
Die Schläger
Definieren Sie die Klasse MovablePaddle
(model/MovablePaddle.js
).
Sie erbt alle Attribute von MovableRectangle
(wk/model/MovableRectangle.js
)
und enthält zunächst nur einen Konstruktor, der genauso definiert wird, wie in wk/model/MovableRectangle.js
.
Ein Schläger wird vom Spieler gesteuert. Er kann ihn (indem er auf der Tastatur entsprechende Tasten drückt) nach unten
oder oben bewengen und er kann ihn wieder anhalten (indem er die jeweilige Steuertaste wieder loslässt).
Ein Schläger hat neben den üblichen Attributen zwei weitere Attribute vyMoving
und
ayMoving
, die festlegen, mit welcher Geschwindigkeit sich der Schläger in y-Richtung bewegen soll,
solange der Spieler die entsprechende Taste bewegt. Schreiben Sie drei Methoden down
,
up
und stop, die dafür sorgen, dass sich der Schläger in die
richtige Richtung bzw. gar nicht bewegt.
down()
{ if (this.vy === 0)
{ this.vy = this.vyMoving;
this.ay = this.ayMoving;
}
}
Die Methode up
wird analog mit umgekehrten Vorzeichen definiert und
die Methode stop
setzt die Geschwindigkeit und die Beschleunigung auf Null.
Importieren Sie MovablePaddle
in game/game01.js
und vergessen
Sie nicht, die Klasse auch in v_json_map
einzutragen. Und weil Sie schon gerade dabei
sind, importieren Sie auch noch die Klasse ViewGraphicsRectangle
aus der <wk>-Library.
Wenn Sie jetzt die Anwendung starten, wird diese Klassen noch nicht verwendet, aber es ist zumindest sichergestellt, dass sie gefunden werden.
Legen Sie jetzt in der Konfigurationsdatei die Modell zweier Paddles samt zugehöriger View an
(wenn beide Paddle gleich aussehen sollen, dann reicht ein View-Objekt, das von beiden Paddle-Modellen referenziert wird).
Als Klasse für die View verwenden Sie ViewGraphicsRectangle
. Diese View Objekte werden genauso
konfigueriert, wie ViewGraphicsCircle
-Objekte.
Die Konfiguration der Paddle-Models ist etwas rafinierter. Bei den Paddles handelt es sich um schlanke (Breite), längliche (Höhe)
Rechtecke , die jeweils parallel zu einer der senkrechten Seiten der Bühne stehen. Setzen Sie den Anchor des linken Paddles in die
Mitte der linken Seite des Rechtecks (xAnchor: 0.0, yAnchor: 0.5
) und den Anchor des rechten Paddles
in die Mitte der rechten Seite des Rechtecks (xAnchor: 1.0, yAnchor: 0.5
).
Das Rechteck (genauer seinen Ankerpunkt) sollten Sie jeweils 5 Pixel vom Bühnenrand entfernt mittig platzieren
(vgl. Musterlösung).
Für die Geschwindiegkeitsattribute, die von den zuvor definierten MEthoden verwendet werden, sollten Sie zunächst folgende Werte verwenden:
"vyMoving": 150,
"ayMoving": 500
Damit wird erreicht, dass der Schläger sich beim Start zunächst langsam bewegt, aber relativ schnell beschleunigt. Später können Sie geeignetere Werte durch Experimentieren ermitteln.
Wenn Sie die Klasse MovablePaddle
korrekt implementiert, die beiden Klassen
MovablePaddle
und ViewGraphicsRectangle
korrekt in das Game-Modul importiert
und die beiden Paddle samt View korrekt konfiguriert haben, sollten sich bei einem Start der App drei Element auf der Bühne
befinden: ein Ball (der sich über die Bühne bewegt) und zwei Schläger (die sich nicht bewegen).
Testhalber können Sie in der Konfigurationsdatei einem Schläger mal eine Geschwindigkeit in y-Richtung zuordnen. Sie werden feststellen, dass er sich permanent zwischen unterem und oberen Rand hin und her bewegt, ohne sich vom vertikalen Bühnenrand wegzubewegen.
Kollissionserkennung und -behandlung
Es ist an der Zeit die Kollissionserkennung und -behandlung anzugehen. Es gibt drei unterschiedliche Fälle:
- Schläger kollidiert mit der Wand: Falls der Schläger mit der Wand kollidert, wird er mittels der zuvor implementierten Methode
stop
angehalten (und zurück auf die Bühne geschoben, falls er in die Wand eingedrungen ist). Es ist dann die Aufgabe des Spielers ihn mittels Tastatursteuerung in Gegenrichtung wieder in Bewegung zu setzen. - Ball kollodiert mit Schläger: Der Ball prallt am Schläger ab. Das funktioniert im Prinzip genauso, wie das Abprallen von der Wand: Bei einer Kollision wird die
x-Geschwindigkeit negiert.
- Ball kollidiert mit der Wand: Falls der Ball mit einer horizontalen Wand kollidiert, prallt er ab, wie bisher auch. Falls er mit einer senkrechten Wand kollidiert (rechte Seite des Balls kleiner linkem Rand der Bühne oder linke Seite des Balls größer rechtem Rand der Bühne!), wird die Logik informiert, dass der Ball von der Bühne verschwunden ist.
Insgesamt sind bei jeder Neuberechnung der Modellwelt fünf Fälle zu überprüfen:
- Kollision vom Ball mit der Bühne
- Kollision von Schläger1 mit der Bühne
- Kollision von Schläger2 mit der Bühne
- Kollision von Schläger1 mit dem Ball
- Kollision von Schläger2 mit dem Ball
Legen Sie zunächst drei Dateien an:
model/CollisionStageBall.js
model/CollisionStagePaddle.js
model/CollisionBallPaddle.js
Fügen Sie jeweils eine Funktion (mit leerem Rumpf ein), die Sie exportieren:
collision(p_stage, p_ball)
collision(p_stage, p_paddle)
collision(p_ball, p_paddle)
Importieren Sie diese Funktionen in die Datei model/ModelUpdate.js
und ersetzen Sie den Befehl collision(p_immovables, p_movables);
durch folgende fünf Befehle:
collisionStageBall (p_models.stage, p_models.ball);
collisionStagePaddle(p_models.stage, p_models.paddle1);
collisionStagePaddle(p_models.stage, p_models.paddle2);
collisionBallPaddle (p_models.ball, p_models.paddle1);
collisionBallPaddle (p_models.ball, p_models.paddle2);
Beachten Sie, dass die Funktion modelUpdate
im Parameter p_models
auf alle Modelle, die in der Konfigurations-Datei im Objekt model
definiert wurden, unter
dem jeweiligen Konfigurationsnamen zugreifen kann. In der Musterlösung heißen die vier benötigten
Objekte stage
, ball
, paddle1
und paddle2
.
Wenn Sie Ihre Modell-Objekte in der Konfigurationsdatei anders benannt haben, müssen Sie die obigen
Befehle entsprechend anpassen.
Anmerkung: Das Modul colision.js
wird hier nicht verwendet. Es wäre sauberer, dieses Modul so zu verallgemeinern, dass es abhängig von den Klassen der zu testenden Objekte die jeweile Kollissionmethode aufrufen würde. Bei vielen Objekten, die kollidieren können, ist das besser, als Dutzende Einzelbefehle zur Kollissionerkennung zu schreiben. Aber hier würde das zu weit führen. Den Import-Befehl
import collision from './collision.js';
sollten Sie daher aus der Datei modelUpdate.js
löschen.
Implementieren Sie nun die drei Methoden.
Kollision vom Ball mit der Bühne
Das ist zunächst ganz einfach. Kopieren Sie den Code aus wk/model/collisionBB.js
und entfernen Sie die Tests
für den linken und den rechten Bühnenrand. Später fügen Sie noch Code ein, der die Logik-Komponente informiert, wenn der Ball die
Bühne verlässt.
Wenn Sie das Programm mehrfach starten, sollte jetzt der Ball vom oberen und unteren Rand abprallen, aber rechts und links von der Bühne verschwinden.
Kollision vom Schläger mit der Bühne
Dieser Test ist noch einfacher. Wenn der obere Rand des Schlägers kleiner oder gleich dem oberen Rand der Bühne ist,
wird der Schläger auf die Bühne zurück geschoben (p_paddle.top = p_stage.top;
) und angehalten p_paddle.stop();
.
Die Kollissionserkennung mit dem unteren Rand erfolgt auf die gleiche Art und Weise.
Kollision vom Ball mit dem Schläger
Das ist eine sehr aufwändige Operation, da 8 Fäclle unterschieden werden müssten: Kollision des Balls mit einer der vier Seiten oder einer der vier Ecken. Folgender recht grober Test muss daher vorerst reichen (der Ball verhält sich allerdings ziemlich eigenartig, wenn er mit einer der Ecke oder Schmalseite des Schlägers kollidiert);
if (p_ball.y + 0.5*p_ball.r >= p_paddle.top &&
p_ball.y - 0.5*p_ball.r <= p_paddle.btm
)
{
if (p_ball.vx > 0 && // The ball is moving from left to right.
p_ball.rgt >= p_paddle.lft && p_ball.lft < p_paddle.rgt
)
{ p_ball.rgt = p_paddle.lft;
p_ball.vx = -p_ball.vx;
}
if (p_ball.vx < 0 && // The ball is moving from right to left.
p_ball.lft <= p_paddle.rgt && p_ball.rgt > p_paddle.lft
)
{ p_ball.lft = p_paddle.rgt;
p_ball.vx = -p_ball.vx;
}
}
Testen Sie Ihre Kollissionsfunktionen, indem Sie in der Konfigurationsdatei unterschiedliche Parameter eingeben: Startposition, Startgeschwindigkeit, Größe des Schlägers, evtl. auch unterrschiedliche Geschwindigkeiten des Balls.
FORTSETZUNG FOLGT
Quellen
- Kowarschick (MMProg): Wolfgang Kowarschick; Vorlesung „Multimedia-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2018; Quellengüte: 3 (Vorlesung)