In Ihrem Projekt finden Sie eine Web-Anwendung: <code>src/index00.html</code>. Diese entspricht im Wesentlichen der Musterlösung der
In Ihrem Projekt finden Sie eine Web-Anwendung: <code>src/index00.html</code>. Diese entspricht im Wesentlichen der Musterlösung der
Aufgabe 3 des Tutoriums [[MMProg: Praktikum: WiSe 2018/19: Ball03|Ball03]]. Allerdings wurde für die Spielbühne eine feste Größe gewählt,
Aufgabe 3 des Tutoriums [[MMProg: Praktikum: WiSe 2018/19: Ball03|Ball03]]. Allerdings wurde für die Spielbühne eine feste Größe gewählt,
das Bild des Balls sowie das CSS wurden verändert und ein Splashscreen wurde eingeführt. diese wird mittels CSS-Transitionen und
das Bild des Balls sowie das CSS wurden verändert und ein Splashscreen wurde eingeführt. Dieser wird mittels CSS-Transitionen und
JavaScript <code>await</code>-Befehlen in der Initfuktion der Datei <code>app00/app.js</code> erreicht. Die <code>await</code>-Befehle
JavaScript <code>await</code>-Befehlen in der Initfunktion der Datei <code>app00/app.js</code> implementiert. Die <code>await</code>-Befehle
sowie die zugehörigen Splaschscreen-Befehle ändern sich im Laufe des Tutoriums nicht. Sie sollten sie aber trotzdem studieren,
sowie die zugehörigen Splaschscreen-Befehle ändern sich im Laufe des Tutoriums nicht. Sie sollten sie aber trotzdem studieren,
wenn Sie daran interessiert sind, wie sie funktionieren.
wenn Sie daran interessiert sind, wie sie funktionieren.
Beachten Sie, dass die Klasse <code>ModelCircle</code> vier GMethoden <code>left</code>, <code>right</code>, <code>top</code> und <code>bottom</code> enthält. Mit diesen Methoden werden
die Ränder des Kreises ermittelt. Damit kann man Kollisionsberechnungen etwas einfacher formulieren.
Bei den Methoden <code>left</code>, <code>right</code>,
<code>top</code> und <code>bottom</code> handelt es sich nicht um normale Methoden, sondern um so
genannte [[Getter-Methode]]n ([https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/get MDN web docs: Getter]). Getter-Methoden sind Methoden ohne Parameter, vor die das Schlusselwort
<code>get</code> geschrieben wird. Sie werden zur Berechnung von Attributwerten verwendet werden.
<source lang="javascript">
get left() { return this.x - this.r; }
// Der linke Rand eines Kreise ist gleich
// seiner Position x abzüglich seines Radius r.
</source>
Eine normale Methode würde man mittels <code>console.log(myCircle.left());</code> aufrufen. Beim Zugriff auf eine Getter-Methode verwendet man dagegen die klammerfreie Attributzugriff-Syntax:
<source lang="javascript">
console.log(myCircle.left);
</source>
Neben den Getter-Methoden gibt es auch noch Setter-Methoden, die dazu dienen,
berechnet Attribute zu verändern ([https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/set MDN web docs: Setter]). Diese haben genau einen Parameter, der den Wert enthält, der
gespeichert werden soll:
<source lang="javascript">
set left(p_x) { this.x = p_x + this.r; }
// Anstelle des linken Randes wird die Position des Kreises geändert,
// und zwar so, dass der linke Rand an der gewünschten Position zu
// liegen kommt.
</source>
Zum Aufruf der Setter-Methoden kommt ebenfalls die Attributzugriff-Syntax zum Einsatz:
<source lang="javascript">
myCircle.left = 100;
console.log(myCircle.left) → 100
</source>
===Use Cases===
===Use Cases===
Zeile 96:
Zeile 133:
*<code>top</code> (analog zu <code>ModelCircle</code> ohne <code>this.r</code>)
*<code>top</code> (analog zu <code>ModelCircle</code> ohne <code>this.r</code>)
*<code>bottom</code> (analog zu <code>ModelCircle</code>, aber mit <code>this.width</code>)
*<code>bottom</code> (analog zu <code>ModelCircle</code>, aber mit <code>this.width</code>)
Die zugehörigen Setter-Methoden werden später noch benötigt. Sie können sie theoretisch auch schon jetzt implementieren.
Darüber hinaus enthält diese Klasse zwei Methoden
Darüber hinaus enthält diese Klasse zwei Methoden
* <code>reset</code> (analog zu <code>ModelCircle</code>)
* <code>reset</code> (analog zu <code>ModelCircle</code>)
* <code>update</code> (analog zu <code>ModelCircle</code>, allerdings muss auch die Geschwindigkeit abhängig von der Beschleunigung angepasst werden; vgl. [[MMProg:_Praktikum:_WiSe_2018/19:_Ball01#Aufgabe_6|Praktikum Ball01, Aufgabe 6]])
* <code>update</code> (analog zu <code>ModelCircle</code>, allerdings muss auch die Geschwindigkeit abhängig von der Beschleunigung angepasst werden; vgl. [[MMProg:_Praktikum:_WiSe_2018/19:_Ball01#Aufgabe_6|Praktikum Ball01, Aufgabe 6]])
Beachten Sie, dass für die Berechnung der Begrenzungen <code>left</code>, <code>right</code>,
<code>top</code> und <code>bottom</code> von Kreis und Rechteck so genannte
[[Getter-Methode]]n zum Einsatz kommen ([https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/get MDN web docs: Getter]).
<source lang="javascript">
get left() { return this.x - this.r; }
// Der linke Rand eines Kreise ist gleich
// seiner Position x abzüglich seines Radius r.
</source>
Bei Getter-Methoden handelt es sich um Methoden ohne Parameter. Sie
werden zur Berechnung von Attributwerten verwendet werden. Eine normale Methode würde man mittels <code>console.log(myCircle.left())</code> aufrufen. Beim Zugriff auf eine Getter-Methode verwendet man dagegen die klammerfreie Attributzugriff-Syntax:
<source lang="javascript">
console.log(myCircle.left)
</source>
Neben den Getter-Methoden gibt es auch noch Setter-Methoden, die dazu dienen,
berechnet Attribute zu verändern ([https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Functions/set MDN web docs: Setter]). Diese haben genau einen Parameter, der den Wert enthält, der
gespeichert werden soll:
<source lang="javascript">
set left(p_x) { this.x = p_x + this.r; }
// Anstelle des linken Randes wird die Position des Kreises geändert,
// und zwar so, dass der linke Rand an der gewünschten Position zu
// liegen kommt.
</source>
Zum Aufruf der Setter-Methoden kommt ebenfalls die Attributzugriffs-Syntax zum Einsatz:
<source lang="javascript">
myCircle.left = 100;
console.log(myCircle.left) → 100
</source>
Die Setter-Methoden werden später noch benötigt. Sie können sie daher auch shcon gleich implementieren.
Beachten Sie bei der Berechnung der der Begrenzungen <code>left</code>, <code>right</code>,
Beachten Sie bei der Berechnung der der Begrenzungen <code>left</code>, <code>right</code>,
<code>top</code> und <code>bottom</code> eines Rechtecks überdies, dass der Ankerpunkt üblicherweise
<code>top</code> und <code>bottom</code> eines Rechtecks, dass der Ankerpunkt üblicherweise
in der linken oberen Ecke des Rechtecks liegt. Beim Kreis liegt er dagegen im Mittelpunkt.
in der linken oberen Ecke des Rechtecks liegt. Beim Kreis liegt er dagegen im Mittelpunkt.
Zeile 145:
Zeile 147:
Die Klasse <code>ViewRectangleGraphics</code> wird analog zu
Die Klasse <code>ViewRectangleGraphics</code> wird analog zu
<code>ViewCircleGraphics</code> implementiert. Der wesentliche Unterschied ist, dass
<code>ViewCircleGraphics</code> implementiert. Der wesentliche Unterschied ist, dass
das grafische Objekt nicht mit <code>drawCircle</code>, sondern mit <code>drawRect</code>
das grafische Objekt nicht mit <code>drawCircle</code>, sondern mittels <code>drawRect</code>
gezeichnet wird ([http://pixijs.download/dev/docs/PIXI.Graphics.html#drawRect PIXI.Graphics]).
gezeichnet wird ([http://pixijs.download/dev/docs/PIXI.Graphics.html#drawRect PIXI.Graphics]).
<!--
==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.
Diese Module müssen Sie entweder erstellen oder – wenn sie schon existieren – an Ihre Gegebenheiten anpassen.
Die grauen Module können Sie direkt aus der <code>wk</code>-Library einbinden, die Sie unter <code>src/js/lib/wk</code> finden.
Der zugehörige Import-Befehl lautet jeweils
<source lang ="javascript">
import ... from 'wk/...';
</source>
Mehrere Beispiele finden Sie in der Datei <code>game01/game.js</code>.
Um die WebStorm-Fehlermeldungen zu vermeiden, die angeben, dass ein <code>wk</code>-Modul
nicht gefunden werden kann, sollten Sie Folgendes machen:
Falls die <code>wk</code>-Modul dann immer noch als fehlerhaft markiert werden,
sollten Sie WebStorm neu starten.
'''Achtung''': In der aktuellen Version 2017.2.5 von WebStorm werden Import-Anweisungen der Art <code>import ... from 'wk/.../MODUL.js';</code>
als fehlerhaft angezeigt. In der ursprünglichen Version des Projektes <code>WK_Pong01_Empty</code> wurden die Module
der <code>wk</code>-Bibliothek noch auf diese Weise importiert. Wenn Sie die WebStorm-Fehlermeldung stört, sollten Sie in
diesen Import-Befehlen die Datei-Endung <code>.js</code> entfernen: <code>import ... from 'wk/.../MODUL';</code>.
(Für Webpack wäre das nicht nötig, da Webpack weiterhin beide Schreibweisen unterstützt.)
Die Importbefehle derjenigen Dateien, deren Importpfad mit <code>./</code> oder <code>../</code> dürfen Sie dagegen nicht ändern.
Diese '''müssen''' auf <code>.js</code> enden, da anderenfalls die zugehörigen Module im Verzeichnis <code>node_modules</code> gesucht werden,
wo sie sich aber nicht befinden.
===Vögel durch einen Ball ersetzen===
Als erstes sollten Sie die Zahl der Vögel reduzieren, indem Sie einfach in der Konfigurationsdatei <code>json/config01.json</code>
den Counter <code>@count</code> in <code>model.bird</code> entfernen. Außerdem sollten Sie den String
<code>"birds"</code> durch <code>"ball"</code> ersetzen und die Array-Klammer <code>[ ]</code> um das Huhn-Model-Konfigurationsobjekt entfernen.
Letzteres ist wichtig, da die Logik – um eine neue Spielrunde starten zu können – direkt, {{dh}} per Namen auf das Ball-Objekt zugreifen wird, sobald es die Bühne verlässt. Ein namentlicher 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 [https://glossar.hs-augsburg.de/beispiel/tutorium/es6/ball/WK_Ball03/src/json/config04a.json Konfigurations-Datei] der
[https://glossar.hs-augsburg.de/beispiel/tutorium/es6/ball/WK_Ball03/web/index04a.html Musterlösung Ball03 (04a)]: <code>view.orange</code> oder <code>view.blue</code>.
Ersetzen Sie in Ihrer Konfigurationsdatei <code>view.hen</code> durch eine der beiden Graphics-Konfigurationen
und ersetzen Sie den View-Bezeichner <code>"hen"</code> unter <code>model.ball.view</code> durch
den von Ihnen gewählten View-Bezeichner.
Wenn Sie jetzt das Programm laufen lassen, fliegt kein Ball mehr über die Bühne.
====<code>config.json</code> und <code>app.js</code>====
In der Browser-Konsole wird folgender Fehler angezeigt:
<source lang ="javascript">
Jetzt können Sie zwei Schläger in Ihre Anwendung einbinden. Erweitern Sie zunächst
v_json_map[c_config_view.class] is not a constructor
die Kondifurationsdatei <code>config/config.json</code>. Fügen Sie in dieser Datei ''in'' das
</source>
Model-Objekt die Model-Konfigurationen der beiden Paddle ein:
Das liegt daran, dass Sie in der Konfigurations-Datei angegeben haben,
dass die Klasse <code>ViewGraphicsCircle</code> (aus <code>wk/view/ViewGraphicsCircle</code>) zum Rendern der
View verwendet werden soll. Das Huhn wurde dagegen mit <code>ViewAnimatedBird</code>
gerendert.
Das Game-Modul <code>game/game01.js</code> 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 <code>v_json_map</code> einfügen.
Verbessern Sie <code>game/game01.js</code> entsprechend.
Wenn alles funktioniert, können Sie den Import der Klasse <code>ViewAnimatedBird</code> aus
<code>game/game01.js</code> entfernen (<code>v_json_map</code> nicht vergessen). Die Datei
<code>view/ViewAnimatedBird.js</code> 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 ({{zB}} Breite: 600 Pixel, Höhe: 400 Pixel)
'''Achtung''': Geben Sie der Konfigurationsdatei jeweils eine Integerzahl und keinen String an!
Das Hintergrundbild ist nicht sehr passend für Pong. Wenn Sie ein besseres haben, ersetzen Sie es (in der Datei <code>src/js/app01.js</code>),
ansonsten löschen Sie in der Konfigurationsdatei einfach das Attribut <code>model.stage.view</code>.
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
Es ist an der Zeit die Kollisionserkennung und -behandlung anzugehen.
Es gibt drei unterschiedliche Fälle:
* Schläger kollidiert mit der Wand: Falls der Schläger mit der Wand kollidiert, wird er mittels der zuvor implementierten Methode <code>stop</code> 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 kollidiert 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:
* <code>model/CollisionStageBall.js</code>
* <code>model/CollisionStagePaddle.js</code>
* <code>model/CollisionBallPaddle.js</code>
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_ball, p_paddle)</code>
Importieren Sie diese Funktionen in die Datei <code>model/ModelUpdate.js</code>
und ersetzen Sie den Befehl <code>collision(p_immovables, p_movables);</code>
Beachten Sie, dass die Funktion <code>modelUpdate</code> im Parameter <code>p_models</code>
auf alle Modelle, die in der Konfigurations-Datei im Objekt <code>model</code> definiert wurden, unter
dem jeweiligen Konfigurationsnamen zugreifen kann. In der Musterlösung heißen die vier benötigten
Objekte <code>stage</code>, <code>ball</code>, <code>paddle1</code> und <code>paddle2</code>.
Wenn Sie Ihre Modell-Objekte in der Konfigurationsdatei anders benannt haben, müssen Sie die obigen
Befehle entsprechend anpassen.
''Anmerkung: Das Modul <code>collision.js</code> 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 jeweilige Kollisionsmethode aufrufen würde. Bei vielen Objekten, die kollidieren können, ist das besser, als Dutzende Einzelbefehle zur Kollisionserkennung zu schreiben. Aber hier würde das zu weit führen. Den Import-Befehl''
<source lang ="javascript">
import collision from './collision.js';
</source>
''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</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 Kollisionserkennung mit dem unteren Rand erfolgt auf die gleiche Art und Weise.
'''Kollision von Ball und Schläger'''
Das ist eine sehr aufwändige Operation, da 8 Fälle 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.
Testen Sie Ihre Kollisionsfunktionen, indem Sie in der Konfigurationsdatei unterschiedliche
Parameter eingeben: Startposition, Startgeschwindigkeit, Größe des Schlägers, evtl. auch unterschiedliche Geschwindigkeiten des Balls.
===Spielsteuerung===
[[Datei:WK Pong01 ClassModel01 control logic.png|gerahmt|ohne|Steuerungsmodule]]
Als nächstes kommt die Spielsteuerung an die Reihe. Die beiden Spieler sollen die der Lage sein,
jeweils einen Schläger mittels Tastatur zu bewegen.
Zum Einsatz kommt hier das [[Observer-Pattern]]: Jedes Mal, wenn ein spezifischer Tastendruck erfolgt,
schickt der Controller <code>PaddleControl</code> eine entsprechende Nachricht an alle Listener. Das
Logik-Modul registriert sich als Listener für derartige Nachrichten, empfängt diese daher künftig und
reagiert auf jede Nachricht, die es erhält, in geeigneter Form.
Sehen Sie sich zunächst einmal die die ursprüngliche Moorhuhn-Anwendung (app00) an.
Dort gibt es einen Controller, der bei einem Klick auf einen
Vogel die Logik darüber informiert. Die Logik reagiert auf eine entsprechende Nachricht, indem sie
den zugehörigen Vogel von der Bühne löscht (mittels der Remove-Funktion, die die Logik vom
Game-Modul erhalten hat).
Die Verknüpfung des Controllers mit den Vögeln erfolgt über das Game-Modul:
In der Konfigurations-Datei <code>config00.json</code> gibt es das Attribut
<code>view.hen.control</code>. Mit diesem Attribut wird der Moorhuhn-View
die Klasse <code>controlBird</code> zugeordnet. Wie üblich wird diese Klasse im Game-Modul
importiert und in der Hashmap <code>v_json_map</code> eingetragen.
Entfernen Sie diesen Controller zunächst vollständig (bevor Sie mit der Implementierung der
Tastatur-Controller anfangen):
* Entfernen Sie – sofern Sie dies noch nicht gemacht haben – das Objekt <code>view.hen</code> aus der Konfigurationsdatei <code>controll01.json</code> (und nicht etwa aus der JSON-Datei <code>config00.json</code>, die Sie zuvor analysiert haben)
* Benennen Sie die Datei <code>control/controlBird.js</code> in <code>control/controlPaddle.js</code> um. Benennen Sie die darin enthaltene Funktion ebenfalls um. Als Parameter wird dieser Controller ein Konfigurationsobjekt <code>p_config</code> erhalten. Den Rumpf der Funktion löschen Sie einfach.
* Ändern Sie den Bezeichner <code>controlBird</code> in der Datei <code>game.js</code> geeignet ab.
* Löschen sie in der Datei <code>logic/logic.js</code> den Inhalt des Rumpf der Funktion <code>init</code>.
Jetzt sollte die Anwendung wieder laufen, ohne dass sich hinsichtlich der Behandlung von Benutzereingaben irgendetwas geändert hätte.
Sie haben ja in dieser Hinsicht auch noch nichts implementiert, sondern nur überflüssigen Ballast entfernt.
Dies folgt als nächster Schritt.
In der Anwendung <code>app00</code> wurden der Controller den View-Objekten
der Moorhühner zugeordnet, da ein Klick auf ein Moorhuhn eine Reaktion des Spiel auslösen sollte.
Diesmal soll der Controller die Tastatur überwachen. Da es sich für die Tastatur kein
View-Objekt gibt, muss die Konfiguration diesmal etwas anders erfolgen.
Das Game-Modul <code>game.js</code> wurde so implementiert, dass es neben der
Erzeugung und Initialisierung von Model- und View-Objekten auch die Initialisierung von Control-Objekten
übernehmen kann.
Fügen Sie in die Datei <code>config01.json</code> hinder dem Objekt <code>view</code>
ein Objekt namens <code>control</code> ein. (Sie könnten es auch vor diesem Objekt oder sogar
vor dem Objekt <code>model</code> einfügen. Die Reihenfolge der Attribute eine Objektes spielt keine Rolle
– zumindest sollte sie keine Rolle spielen.) In das Objekt <code>control</code> fügen Sie zwei Objekte
<code>paddle1</code> und <code>paddle2</code> zur Konfiguration der Steuerung der beiden Schläger ein.
Jedes dieser beiden Objekte enthält vier Attribute:
* <code>control</code>: Hier muss der Name der zugehörigen Controller-Funktion angegeben werden. Im Fall der beiden Schläger ist dies die (bereits existierende aber noch nicht implementierte) Funktion <code>controlPaddle</code>.
* <code>model</code>: Hier muss der Name des zu kontrollierenden Models angegeben werden. Im Model-Objekt weiter oben in der Konfigurationsdatei hatten Sie die Konfigurationen für zwei Schläger angelegt. Geben Sie im Controller jeweils den Namen eines dieser beiden Objekte an.
* <code>up</code> und </code>down</code>: Hier muss jeweils ein Objekt angegeben werden, die eine Keyboard-Taste beschreibt. Beispielsweise beschreibt das Objekt <source lang="javascript">
{"key": "ArrowUp", "keyCode": 38}</source> die Pfeil-nach-oben-Taste. (Leider ist es derzeit noch notwendig sowohl den Namen einer Taste, als auch den zugehörigen Key-Code anzugeben, da beispielsweise Android-Geräte mit (Bluetooth-)Tastatur die modernere Key-Variante noch nicht beherrschen.<ref>[https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key MDN web docs – KeyboardEvent.key]</ref>). Allerdings ist die Verwendung von Key-Codes als ''deprecated'' (''angelehnt''/''überholt'') markiert worden, das die Codes browser- und tastaturabhängig sind (siehe {{zB}} [https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode MDN web docs – KeyboardEvent.keyCode]<ref> [https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode MDN web docs – KeyboardEvent.keyCode]</ref>). Man sollte also bei einem Tastaturevent immer erst überprüfen, ober der Bezeichner der Taste den Vorgaben entspricht, bevor man auf den Key-Code zurückgreift. '''Tipp:''' Auf der Seite http://keycode.info/ können Sie eine beliebige Taste drücken und erhalten beides <code>keyCode</code> und <code>key</code>.
Sobald Sie die beiden Controller konfiguriert haben, sollten Sie Ihre Anwendung testen. Sie sollte fehlerfrei durchlaufen, aber auf Tastaturereignisse reagiert sie noch nicht.
Es ist aber schon gut zu wissen, dass der Programmstart keinen Fehler zur Folge hat. Wenn sie das Modul <code>controlPaddle</code> nicht sauber ins Game-Modul
integriert haben, erhalten Sie einen Fehler: <code>v_json_map[l_config.control] is not a function</code>. Probieren Sie es aus, indem Sie in der Datei
<code>config01.json</code> einmal absichtlich einen Schreibfehler in das Wort <code>controlPaddle</code> einfügen.
Jetzt ist es an der Zeit, die Funktion <code>controlPaddle</code> (in der Datei <code>control/controlPaddle.js</code>) zu implementieren.
Schreiben Sie testhalber mal den Befehl
<source lang="javascript">
console.log(p_config);
</source>
in den Rumpf dieser Funktion. Im Konsolfenster des Browsers sollten beim Programmstart die beiden zuvor erstellten Konfigurationsobjekte ausgegeben werden.
Wie man ein Tastaturereignis abfängt (mit <code>window.addEventListener()...</code>) und behandelt (mittels Callback-Funktion)
sollte Ihnen bekannt sein: [[HTML5-Tutorium: JavaScript: Hello World 03]], insbesondere Datei <code>[https://glossar.hs-augsburg.de/beispiel/tutorium/es6/hello_world/WK_HelloWorld03/main.js main.js]</code>.
Fügen Sie '''in''' den Rumpf der Funktion <code>controlPaddle</code> zwei Funktionen <code>o_start_moving</code> und <code>o_stop_moving</code>
(Präfix <code>o_</code> da es sich um [[Observer]]-Funktionen handelt; observer = event listener).
jeweils mit Parameter <code>p_event</code> ein. Sorgen Sie dann mittels geeigneter <code>window.addEventistener</code>-Anweisungen dafür, dass die
erste Funktion bei einem <code>keyDown</code>-Event ausgeführt wird und die zweite bei einem <code>keyUp</code>-Event. Bei jedem Aufruf soll
die zugehörige Observer-Funktion ihren eigenen Namen sowie den Key-Bezeichner und den Key-Code der Taste ausgeben, die den Event veranlasst hat.
Wenn alles wunschgemäß funktioniert, sollten Sie jetzt abhängig von dem jeweiligen Tastendruck eine geeignete Nachricht per Event-Dispatcher verschicken.
Löschen sie in der Datei <code>control/control.js</code> die überflüssige „Konstante“ <code>control.C_EVENT_BIRD_HIT</code> und fügen Sie
dafür für die drei möglichen Schläger-Ereignisse drei neue Konstanten ein:
<source lang="javascript">
control.C_EVENT_PADDLE_UP = 'EventPaddleUp';
control.C_EVENT_PADDLE_DOWN = 'EventPaddleDown';
control.C_EVENT_PADDLE_STOP = 'EventPaddleStop';
</source>
Gehen Sie zurück in die Datei <code>control/controlPaddle.js</code>.
Dort sehen Sie, dass das Event-Dispatcher-Objekt <code>control</code>, für das Sie gerade ein paar Konstanten definiert haben,
bereits importiert wird. Sie können dieses Objekt verwenden, um Nachrichten an
andere interessierte Objekte zu verschicken.
Fügen Sie an den Anfang des Rumpfs der Funktion <code>controlPaddle</code> ein paar Konstantendefinitionen ein, die die Attribute des Konfigurationsobjektes
speichern:
<source lang="javascript">
const
c_model_name = p_config.model,
c_key_up = p_config.up.key,
c_keycode_up = p_config.up.keyCode,
c_key_down = p_config.down.key,
c_keycode_down = p_config.down.keyCode;
</source>
Definieren Sie im Anschluss an die Konstanten eine Hilfsfunktion zum Dispatchen eines neuen Events:
und <code>control.C_EVENT_PADDLE_STOP</code> beschränken.
Die Nachricht, {{dh}} das Event-Objekt, das verschickt wird, wird mittels <code>new CustomEvent(p_type, p_init)</code>
erstellt (siehe [https://developer.mozilla.org/de/docs/Web/API/CustomEvent MDN web docs – CustomEvent] und auch [https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events MDN web docs – Creating and triggering events]<ref>[https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events MDN web docs – Creating and triggering events]</ref>).
Als Detail-Information, auf wen sich das Ereignis bezieht, wird der im Konfigurationsobjekt gespeicherte Name
<code>p_config.model</code> (bzw. <code>c_model_name</code>) an den Empfänger der Nachricht übermittelt.
Implementieren Sie die Funktion <code>o_start_moving</code> so, dass das Ereignis <code>control.C_EVENT_PADDLE_UP</code> (mittels der zuvor definierten
Hilfsfunktion) verschickt wird, wenn die im Konfigurationsobjekt angegebene „Up“-Taste betätigt wurde, und das Ereignis code>control.C_EVENT_PADDLE_DOWN</code>,
falls die „Down“-Taste betätigt wurde. Die Funktion <code>o_stop_moving</code> soll das Ereignis <code>control.C_EVENT_PADDLE_STOP</code> versenden,
sobald eine der genannten Tasten losgelassen wird.
'''Tipp''': Wenn Sie vor der Funktion <code>controlPaddle</code> den folgenden Kommentar einfügen,
<source lang="javascript">
/**
* @param p_config {{model: String,
* up: {key: String, keyCode: Number},
* down: {key: String, keyCode: Number},
* }
* }
*/
</source>
zeigt Ihnen WebStorm weniger Warnings an. Die Typdefinition, die in geschweiften Klammern hinter dem Parameter <code>p_config</code>
angegeben wird, beschreibt, wie das Konfigurationsobjekt aufgebaut sein sollte. Tatsächlich enthält das Objekt, das übergeben wird noch
ein weiteres Attribut (nämlich <code>"control": "controlPaddle"</code>). Das wird aber vom Game-Modul benötigt, um die korrekte Control-Funktion aufzurufen,
die Control-Funktion selbst benötigt dieses Attribut nicht mehr.
Wie können Sie nun Ihren Code testen? Ganz einfach, indem Sie zugehörige Event-Listener schreiben.
Wo dieser definiert wird, ist dem Event-Dispatcher vollkommen egal. Daher schreiben Sie die die Tests einfach gleich in das Modul
<code>controlPaddle</code> selbst hinter die Funktion <code>controlPaddle</code>.
<source lang="javascript">
control.addEventListener
( control.C_EVENT_PADDLE_DOWN,
function(p_event)
{ console.log('down', p_event.detail); }
);
</source>
(Analog für die beiden anderen Event-Typen.)
====Behandlung von Tastaturevents durch die Logik====
Wenn alles klappt, {{dh}}, wenn Sie im Konsolfenster des Browsers korrekt benachrichtigt werden,
sobald Sie der in der Konfigurationsdatei spezifizierten Tasten drücken, sollten Sie die drei Testbefehle
ans Ende des Rumpfes der <code>init</code>-Funktion in der Datei <code>logic/logic.js</code> einfügen.
Da das Logikmodul ebenfalls das Objekt <code>control</code> importiert, sollte die Anwendung immer
noch funktionieren. Das heißt, im Konsolfenster des Browsers sollten weiterhin die Tastenereignisse protokolliert werden.
Im Logikmodul haben Sie Zugriff auf die Modelle, die vom Game-Modul erzeugt wurden: <code>p_models</code>.
Im Parameter <code>p_event</code> der drei Eventlistener erhalten Sie '''den Namen''' des Objektes, das mittels
des aktuellen Tastaturevents gesteuert werden soll: <code>p_event.detail</code>. Damit ist es möglich,
das Schläger-Model-Objekt selbst zu ermitteln: <code>p_models[p_event.detail]</code>. Für diese Objekte hatten Sie zuvor
drei Methoden definiert: <code>up</code>, <code>down</code> und <code>stop</code>.
Rufen Sie in jedem der drei Eventhandler die jeweils passende Methode auf. Nun sollten die beiden Paddles gesteuert werden können.
(Falls alles funktioniert, sollten Sie die <code>console.log</code>-Befehle in den Eventhandler auskommentieren oder löschen.)
====Behandlung von Ball-Events durch die Logik====
Jetzt fehlt noch die Reaktion der Logik auf das Verschwinden des Balls.
Fügen Sie in die Datei <code>control/control.js</code> zwei weitere „Konstanten“ ein:
Nun können Sie jeweils einen Aufruf dieser Hilfsmethode in die beiden Event-Listener für die Ereignisse <code>C_EVENT_BALL_LEAVES_LEFT</code>
und <code>C_EVENT_BALL_LEAVES_RIGHT</code> einfügen.
Ab sofort sollte sofort, wenn der Ball die Bühne verlässt, ein neuer Ball in der Mitte der Bühne
erzeugt werden und sich in zufällige Richtung los bewegen. (Wenn Sie Ihre Bühne nicht 600 mal 400 Pixel groß gewählt haben,
müssen Sie die x- und die y-Position des Balls anpassen, damit er von der Mitte der Bühne aus startet.)
''Anmerkung'': Man könnte auch noch ein Timer-Objekt ([https://developer.mozilla.org/de/docs/Web/API/WindowTimers/setTimeout WindowTimers.setTimeout()]) erstellen,
das das neue Ball-Objekt erst nach einer gewissen Zeitspanne erzeugt ({{zB}} 500 Millisekunden). Dazu müsste allerdings das Logik-Objekt
auf die Game-Loop zugreifen können, um das Spiel anzuhalten., während kein Ball im Spiel ist. Das ist etwas für Pong02.
Eine Unsauberkeit sollten Sie noch beheben. Die Definition der Konstanten <code>c_init_ball</code> im Modul <code>logic</code>
widerspricht dem Prinzip „[[Programmierprinzipien|Schreibe konfigurierbaren Code]]“. Wenn Sie beispielsweise in der Konfigurationsdatei <code>config01.json</code>
die Grüße der Bühne ändern, müssen Sie zurzeit zusätzlich in der Datei <code>logic/logic.js</code> die Position des Balls im Objekt
<code>c_init_ball</code> ändern, damit er weiterhin von der Mitte aus startet. Die Erfahrung zeigt, dass das regelmäßig vergessen wird. Außerdem
findet man, wenn die Programmierung schon etwas zurück liegt, die Datei, in der die Änderung erfolgen muss, {{iAllg}} nur schwer.
Das Objekt, das derzeit in der Konstanten <code>c_init_ball</code> gespeichert wird, sollte unbedingt in der Datei <code>config01.json</code> gespeichert
werden und nicht in der Datei <code>logic.js</code>. Dabei gibt es allerdings zwie Schwierigkeiten:
# Das Ball-Objekt soll nicht zum Startzeitpunkt der Anwendung erstellt werden, sondern vom Logik-Modul, wenn ein neues Spiel gestartet wird. Daher kann das Initialisierungsobjekt nicht im Objekt <code>model</code> innerhalb der Konfigurationsdatei werden. Alle Model-Objekte, deren Konfiurationsinformationen dort abgelegt sind, werden bereits zum Startzeitpunkt erstellt.
# Jedesmal, wenn ein neues Ball-Objekt vom Logik-Modul erzeugt wird, soll es eine zufällige Geschwindigkeit haben. Das erfolgt mit Spezifikationen der Art <code>"vx": {"@min": 150, "@max": 300, "@positive": 0.5}</code>. Die Funktion <code>concretize</code> aus dem Modul <code>wk/Util</code> ersetzt das Objekt <code>{"@min": 150, "@max": 300, "@positive": 0.5}</code> durch einen Wert zwischen <code>150</code> und <code>300</code> und negiert diesen Wert in 50% der Fälle. Allerdings wird die Funktion <code>concretize</code> schon bei Programmstart auf die gesamte Konfigurationsdatei angewendet, so dass ein Element des Konfigurationsobjekts, das irgendeinem Modul übergeben wird, nie ein derartiges Konstrukt enthält. Es enthält stattdessen einen zufällig gewählten Wert aus dem angegebenen Bereich, der sich aber nicht mehr ändert.
Beide Probleme können mit der aktuellen Implementierungen des Game-Moduls und der Funktion <code>concretize</code>.
ad 1.) Objekte, die im Konfigurationsobjekt <code>init</code> eingefügt werden, werden dem Logik-Modul im Parameter <code>p_init</code> zur Verfügung gestellt (allerdings in konkretisierter Form).
ad 2.) Die Funktion <code>concretize</code> ersetzt einen doppelten Klammeraffen durch einen einfachen Klammeraffen. Das heißt, die Konkretisierung von
<source lang="javascript">
{"@min": 150, "@max": 300, "@positive": 0.5}
</source>
liefert eine Zahl zwischen 150 und 300 oder zwischen -300 und -150 als Ergebnis,
wohingegen die Konkretisierung von
<source lang="javascript">
{"@@min": 150, "@@max": 300, "@@positive": 0.5}
</source>
das Objekt <code>{"@min": 150, "@max": 300, "@positive": 0.5}</code> als Ergebnis liefert.
Damit lässt sich das oben beschriebene Problem beheben. Fügen Sie in die Datei <code>config02</code>
folgendes Objekt vor dem Objekt <code>model</code> ein:
Fügen Sie in die Funktion <code>init</code> des Moduls <code>logic</code> dem Befehl <code>console.log(p_init);</code> und sehen Sie sich das Ergebnis an.
Nun können sie die Konstante <code>c_init_ball</code> aus dem Logik-Modul löschen und in der Funktion <code>f_new_ball</code> den Aufruf
<code>concretize(c_init_ball)</code> durch <code>concretize(p_init.ball)</code> ersetzen.
Jetzt fehlt nur noch die Ausgabe des Punktestands. Für die Verwaltung des Punktestands brauchen Sie kein Punkte-Model. Es reichen
zwei Variablen <code>v_score_player1</code> und <code>v_score_player2</code>, die jedes Mal, wenn der Ball
die Bühne verlässt entsprechende der Seite, auf der der Ball die Bühne verlassen hat, um Eins erhöht wird.
Für die Ausgabe des Textes benötigen Sie die beiden Module <code>model/ImmovableText.js</code> und <code>view/ViewText.js</code>.
Im ersten Modul die Klasse <code>ImmovableText</code> definiert, die alle Properties der Klasse <code>Immovable</code>
erbt und das Attribut <code>text</code> zur Propertyliste hinzufügt. Da wird mit dem Befehl <code>super(mixin ({text: ''}, p_config));</code>
im Konstruktor erledigt. (Zur Erinnerung: Den Befehl <code>mixin</code> finden Sie im Modul <code>wk/util</code>.)
Die Klasse <code>ViewText</code> wird analog zur Klasse <code>ViewGraphics</code> (<code>wk/view/ViewGraphics</code>)
erstellt. Nur wird diesmal kein Grafik-Objekt erstellt (<code>new p_pixi.Graphics()</code>), sondern ein Text-Objekt
(<code>new p_pixi.Text(p_model.text, p_config)</code>), dem der aktuelle Text aus dem Model-Objekt sowie das Konfigurationsobjekt
mit beliebigen [http://pixijs.download/dev/docs/PIXI.TextStyle.html Style-Attributen] als Argumente übergeben wird.
Wichtig ist noch, dass Sie eine Update-Funktion einfügen, damit die View stets den aktuell im Modell gespeicherten Text anzeigt:
<source lang="javascript">
update()
{ super.update();
this.sprite.text = this.model.text;
}
</source>
Wie bereits ziemlich zu Beginn des Dokument erwähnt wurde, wird in Pong02 die Update-Funktion durch einen Event-Handling-Mechanismus ersetzt:
Jedes Mal, wenn sich im Model etwas ändert, werden alle zugehörigen View-Objekte (es kann durchaus mehr als ein View-Objekt für ein Model-Objekt
geben) über die Änderung informiert. In diesem Moment aktualisieren die View die zugehörige Darstellung. Damit ist die Update-Methode
im View-Objekt überflüssig. Diese aktualisiert derzeit '''jedes Mal''', bevor ein View-Objekt auf die Bühne gezeichnet wird, den darzustellenden Inhalt,
auch wenn sich das zugehörige Model gar nicht verändert hat und somit an der Darstellung gar nichts geändert werden muss. Der
Event-Handling-Mechanismus sorgt dafür, dass View-Updates nur dann durchgeführt werden, wenn sich im zugehörigen Model auch etwas geändert hat.
Nun ist es an der Zeit, die beiden View-Objekte in die Anwendung einzubinden.
Dabei gehen Sie wie üblich vor:
* Importieren der Module code>ImmovableText</code> und <code>ViewText</code> in das Game-Modul. Außerdem muss die JSON-Map muss geeignet werden.
* Konfiguration zweier Text-Objekte samt zugehöriger View (mit [http://pixijs.download/dev/docs/PIXI.TextStyle.html PIXI.TextStyle]-Attributen!). Positionieren Sie das eine Text-Objekt in der linken Spielfeldhälfte und das andere in der rechten. Denken Sie daran, das Anchoring von Texten funktioniert noch nicht.
* Ändern des Textattribut-Wertes im Model-Objekt, wann immer ein Spieler einen Punkt erhält.
===Abschlussarbeiten===
Haben Sie eigentlich jedes Mal daran gedacht, in Dateien, die Sie erstellt haben,
Ihren Namen ins Kommentarfeld <code>@author</code> zu schreiben?
Es fehlt auch noch die Implementierung des Use Case: „Spielende, sobald einer der Spieler 10 Punkte erreicht hat“. Auch dafür muss das Logik-Modul
Nun ist es an der Zeit die Datei <code>app.js</code> anzupassen:
auf die Game-Loop zugreifen können. Daher ist auch dies ein Punkt, der erst in Pong02 behandelt wird.
* Importieren Sie die beiden neu erstellten Klassen <code>ModelRectangle</code> und <code>ViewRectangleGraphics</code> analog zu <code>ModelCircle</code> und <code>ViewCircleGraphics</code>. Auch hier gilt: Wenn Sie in beiden DAteien keine Syntaxfehler gemacht haben, lässt sich die App immer noch fehlerfrei übersetzen
Ziel dieser Praktikumsaufgabe ist es, das Spiel Pong zu implementieren.
Vorbereitung
Importieren Sie das leere Git-Projekt Pong01 in WebStorm.
Laden Sie anschließend mittels npm i alle benötigten Node.js-Module in das Projekt.
Sie können Ihr Projekt zur Übung auch in Ihrem Git-Repository speichern. Das ist aber nicht so wichtig.
Falls Sie das machen möchten, müssen Sie es zuvor von meinem (schreibgeschützten) Repository lösen:
In Ihrem Projekt finden Sie eine Web-Anwendung: src/index00.html. Diese entspricht im Wesentlichen der Musterlösung der
Aufgabe 3 des Tutoriums Ball03. Allerdings wurde für die Spielbühne eine feste Größe gewählt,
das Bild des Balls sowie das CSS wurden verändert und ein Splashscreen wurde eingeführt. Dieser wird mittels CSS-Transitionen und
JavaScript await-Befehlen in der Initfunktion der Datei app00/app.js implementiert. Die await-Befehle
sowie die zugehörigen Splaschscreen-Befehle ändern sich im Laufe des Tutoriums nicht. Sie sollten sie aber trotzdem studieren,
wenn Sie daran interessiert sind, wie sie funktionieren.
Beachten Sie, dass die Klasse ModelCircle vier GMethoden left, right, top und bottom enthält. Mit diesen Methoden werden
die Ränder des Kreises ermittelt. Damit kann man Kollisionsberechnungen etwas einfacher formulieren.
Bei den Methoden left, right,
top und bottom handelt es sich nicht um normale Methoden, sondern um so
genannte Getter-Methoden (MDN web docs: Getter). Getter-Methoden sind Methoden ohne Parameter, vor die das Schlusselwort
get geschrieben wird. Sie werden zur Berechnung von Attributwerten verwendet werden.
getleft(){returnthis.x-this.r;}// Der linke Rand eines Kreise ist gleich // seiner Position x abzüglich seines Radius r.
Eine normale Methode würde man mittels console.log(myCircle.left()); aufrufen. Beim Zugriff auf eine Getter-Methode verwendet man dagegen die klammerfreie Attributzugriff-Syntax:
console.log(myCircle.left);
Neben den Getter-Methoden gibt es auch noch Setter-Methoden, die dazu dienen,
berechnet Attribute zu verändern (MDN web docs: Setter). Diese haben genau einen Parameter, der den Wert enthält, der
gespeichert werden soll:
setleft(p_x){this.x=p_x+this.r;}// Anstelle des linken Randes wird die Position des Kreises geändert,// und zwar so, dass der linke Rand an der gewünschten Position zu// liegen kommt.
Zum Aufruf der Setter-Methoden kommt ebenfalls die Attributzugriff-Syntax zum Einsatz:
myCircle.left=100;console.log(myCircle.left)→100
Use Cases
Use Cases des Spiels Pong
Der Aufgabe liegt das Modell Pong/Modellierung zugrunde. Allerdings wurden bei der Umsetzung einige Änderungen vorgenommen.
Am Use-Case-Diagramm fällt auf, dass der Use Case „Spiel abbrechen“ fehlt. Es wurde darauf verzichtet, da nicht – wie ursprünglich geplant –
ein Start-/Stopp-Knopf (als HTML-Button) außerhalb der Bühne platziert wird, sondern nun ein Start-Knopf innerhalb der Bühne. Dieser wird
ausgeblendet, solange das Spiel läuft.
Ich habe das implementierte Modell ganz bewusst gegenüber der ursprünglichen Planung abgeändert,
um den dynamischen Prozess zu verdeutlichen, den ein Modell durchläuft. Es ändert sich ständig:
Elemente werden ergänzt, verfeinert, ersetzt oder auch ersazlos gestrichen. Ich kenne niemanden,
der zu Beginn eines Projektes ein perfektes Modell aufstellt, dass nicht ein paar Dutzend mal geändert werden muss.
Dieses Klassendiagramm ist ebenfalls gegenüber dem Klassendiagramm von Pong/Modellierung weiterentwickelt worden.
Anstelle eines HTML-Elements, gibt es jetzt einen kreisförmigen Start-Button. Die Klassen ModelCircle und
ViewCircle werden auch für den Ball verwendet (Wiederverwendung). Die Klasse ModelPaddle
wurde als Unterklasse einer (wiederverwendbaren) Klasse ModelRectangle definiert. Für die Punkteanzeige
wurde eine eigene Klasse ModelScore definiert, dan in der Klasse ModelText keine Zahlen,
sondern nur Text geespiechert werden können. Das ist für die Rechnung mit Punkten, die jeweils mittels ++
erhöht können werden müssen, etwas unpraktisch. Zu guter Letzt wurde noch ein Textfeld eingeführt, das dazu genutzt
werden kann, bei Spielende die Spieler über den Sieger zu informieren.
Aufgabe
Implementieren Sie Pong gemäß obigem Klassen Diagramm.
right (analog zu ModelCircle, aber mit this.width)
top (analog zu ModelCircle ohne this.r)
bottom (analog zu ModelCircle, aber mit this.width)
Die zugehörigen Setter-Methoden werden später noch benötigt. Sie können sie theoretisch auch schon jetzt implementieren.
Darüber hinaus enthält diese Klasse zwei Methoden
reset (analog zu ModelCircle)
update (analog zu ModelCircle, allerdings muss auch die Geschwindigkeit abhängig von der Beschleunigung angepasst werden; vgl. Praktikum Ball01, Aufgabe 6)
Beachten Sie bei der Berechnung der der Begrenzungen left, right,
top und bottom eines Rechtecks, dass der Ankerpunkt üblicherweise
in der linken oberen Ecke des Rechtecks liegt. Beim Kreis liegt er dagegen im Mittelpunkt.
ViewRectangleGraphics
Die Klasse ViewRectangleGraphics wird analog zu
ViewCircleGraphics implementiert. Der wesentliche Unterschied ist, dass
das grafische Objekt nicht mit drawCircle, sondern mittels drawRect
gezeichnet wird (PIXI.Graphics).
config.json und app.js
Jetzt können Sie zwei Schläger in Ihre Anwendung einbinden. Erweitern Sie zunächst
die Kondifurationsdatei config/config.json. Fügen Sie in dieser Datei in das
Model-Objekt die Model-Konfigurationen der beiden Paddle ein:
Und in das View-Objekt fügen Sie eine View-Konfiguration ein, die für beide Paddle verwendet werden kann.
"paddle":{"border":0,"color":{"color":"#999999"}}
Wenn Sie keinen Syntaxfehler gemacht haben, lässt sich die app01 immer noch fehlerfrei übersetzen.
Nun ist es an der Zeit die Datei app.js anzupassen:
Importieren Sie die beiden neu erstellten Klassen ModelRectangle und ViewRectangleGraphics analog zu ModelCircle und ViewCircleGraphics. Auch hier gilt: Wenn Sie in beiden DAteien keine Syntaxfehler gemacht haben, lässt sich die App immer noch fehlerfrei übersetzen