|
|
| Zeile 1: |
Zeile 1: |
| {{HTML5-Tutorium:Canvas:MiniPong:Menü}} | | {{HTML5-Tutorium:Canvas:MiniPong:Menü}} |
| '''Musterlösung''': <code>[http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03/WebContent/index.html Minipong 3]</code> | | '''Musterlösung''': <code>[https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong04/web/index.html index.html]</code> |
| ([http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03 SVN-Repository]) | | ([https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong04/ WK_MiniPong04 (SVN)]) |
|
| |
|
| =Ziel: Implementierung von MiniPong mit Schläger, Ball und Punktanzeige= | | ==Ziel: Das fertige Spiel „MiniPong“== |
| Im dritten Teil des Tutoriums wird beschrieben, wie man MiniPong mit Hilfe einer einzigen JavaScript-Datei <code>main.js</code> erstellt (wenn man von der Auslagerung der Konstanten in eine
| | [[Datei:MiniPong04 Model Overview.png|gerahmt|rechts|Klassendiagramm]] |
| eigene „Initialisierungsdatei“ einmal absieht).
| |
| [[Kowarschick|Wolfgang Kowarschick]] bezeichnet so eine Datei als „Moloch-Datei“ (Moloch = Monstrum, Ungeheuer, alles verschlingende Bestie).
| |
|
| |
|
| Drei Objekte werden implementiert: Das ROOT-Objekt der Anwendung, ein Ball-Objekt und ein Schläger-Objekt.
| | Im vierten Teil des Tutoriums wird eine funktionsfähige Version von MiniPong erstellt. |
| [[Datei:MiniPong03Canvas.png|miniatur|ohne|582px|Das Objektmodell von MiniPong 03]]
| |
|
| |
|
| Anmerkung: Darüber hinaus gibt es noch ein Info-Objekt, zur Ausgabe von Informationen über den Spielverlauf.
| |
| Das sich dieses Text-Objekt jedoch nicht auf der Bühne befindet, wurde es im obigen Modell nicht aufgeführt.
| |
| Das heißt, das obige Modell ist genaugenommen unvollständig.
| |
|
| |
| =Anwendung „<code>MiniPongCanvas03</code>“=
| |
|
| |
|
| ==Neues Projekt anlegen== | | ==Neues Projekt anlegen== |
|
| |
|
| Legen Sie ein neues „Statisches Web-Projekt“ mit dem Namen <code>MiniPongCanvas03</code> an. | | Legen Sie ein neues Projekt mit dem Namen „<code>MiniPong04</code>“ an. |
| | |
| Speichern Sie dieses Projekt wie üblich in Ihrem Repository.
| |
| | |
| ==Dateien erstellen==
| |
| | |
| Kopieren Sie die Dateien <code>index.html</code> und <code>css/Main.css</code> von [[HTML5-Tutorium: Canvas: MiniPong 02|Teil 2]] des Tutoriums,
| |
| passen Sie den Projekttitel in der Datei <code>index.html</code> an
| |
| und erstellen Sie die Dateien <code>js/CONSTANT.js</code> <code>js/Main.js</code> neu.
| |
| | |
| ===<code>index.html</code>===
| |
| | |
| Fügen Sie in den HTML-Rumpf (<code>body</code>) folgenden Code ''vor'' das <code>canvas</code>-Element
| |
| ein:
| |
| | |
| <source lang="javascript">
| |
| <p id="d_info"> </p>
| |
| </source>
| |
| | |
| Der Text dieses Elements wird von JavaScript aus geändert, um den jeweiligen Punktestand anzuzeigen.
| |
| <code>&nbsp;</code> ist ein nicht-trennbares Leerzeichen (Non-Breaking SPace) und sorgt dafür,
| |
| dass sich Eclipse nicht über ein leeres <code>p</code>-Element beschwert.
| |
| | |
| Fügen Sie in den HTML-Rumpf (<code>body</code>) folgendes Formular ''hinter'' das <code>canvas</code>-Element
| |
| ein:
| |
| | |
| <source lang="javascript">
| |
| <form>
| |
| <input id="d_start_stop" type="button" value=" "/>
| |
| </form>
| |
| </source>
| |
| | |
| Dieser Button fungiert – je nach aktuellen Status des Spiels – als Start- oder Stopp-Button.
| |
| Die Beschriftung sowie die Zuordnung der zugehörigen Aktion wird jeweils mittels JavaScript
| |
| an den aktuellen Zustand der Anwendung angepasst.
| |
| | |
| Das Attribut <code>value</code> muss laut HTML5-Standard vorhanden sein.
| |
| Dessen Wert dar nicht „leer“ sein. Daher wird es hier angegeben, obwohl
| |
| es eigentlich unnötig ist, da der Wert sofort von JavaScript überschrieben wird.
| |
| | |
| ===<code>CONSTANT.js</code>===
| |
| | |
| Fügen Sie alle Variablen der beiden Dateien <code>CONSTANT.js</code> von [[HTML5-Tutorium: Canvas: MiniPong 01#CONSTANT.js|Teil 1]]
| |
| und [[HTML5-Tutorium: Canvas: MiniPong 02#CONSTANT.js|Teil 2]] des Tutoriums in die Datei <code>CONSTANT.js</code> ds dritten Teils ein.
| |
| | |
| '''Achtung: Definieren Sie keine Variable doppelt.'''
| |
| | |
| Sie werde später noch weitere Konstanten in diese Datei einfügen,
| |
| wie z.B. Konstanten, die die Startposition von Ball und Schläger definieren,
| |
| oder eine Konstate <code>PADDLE_FRICTION</code>, die die Reibung des Schlägers festlegt.
| |
| (Beachten Sie, dass in den Use Cases Folgendes gefordert wurde:
| |
| ''Wird der Schläger im Moment der Kollision bewegt, so wird der Ball abhängig von der Bewegungsrichtung und Geschwindigkeit des Schlägers abgelenkt.'')
| |
| | |
| ===<code>main.js</code>===
| |
| | |
| Fügen Sie die Variablen <code>g_ball</code>, <code>g_paddle</code>, <code>g_context</code>, <code>g_imterval</code>
| |
| der beiden Dateien <code>main.js</code> von [[HTML5-Tutorium: Canvas: MiniPong 01#main.js|Teil 1]]
| |
| und [[HTML5-Tutorium: Canvas: MiniPong 02#main.js|Teil 2]] des Tutoriums in die Datei <code>main.js</code> des dritten Teils ein.
| |
| Definieren Sie zusätzlich eine globale Variable <code>g_score</code>, in der der aktuelle Punktestand des Spiels abgelegt wird.
| |
| | |
| ====Das Ball-Objekt <code>g_ball</code>====
| |
| Erweitern Sie das Ball-Objekt <code>g_ball</code> um eine Resetmethode, deren Aufgabe es ist, den Ball
| |
| vor Spielbeginn zur Startposition zu verschieben. Außerdem muss diese Funktion eine Startgeschwindikeit
| |
| des Balls festlegen.
| |
| | |
| <source lang="javascript">
| |
| // Moves the ball to its starting position
| |
| // and computes randomly a starting velocity vector.
| |
| reset:
| |
| function()
| |
| { this.x = BALL_X;
| |
| this.y = BALL_Y;
| |
| this.vx = (Math.random()<0.5?1:-1)
| |
| * (BALL_VX_MIN+Math.random()*(BALL_VX_MAX-BALL_VX_MIN));
| |
| this.vy = (BALL_VY_MIN+Math.random()*(BALL_VY_MAX-BALL_VY_MIN));
| |
| },
| |
| </source>
| |
| | |
| Definieren Sie die Konstanten <code>BALL_X</code> und <code>BALL_Y</code> in der Datei <code>CONSTANT.js</code> so, dass
| |
| der Ball in der Mitte des oberen Spielfeldrands zum liegen kommt. Legen Sie für die übrigen Konstanten
| |
| sinnvolle Werte fest.
| |
| | |
| Legen Sie im Ball-Objekt für alle [[Attribut]]e sinnvolle Initialwerte fest.
| |
| Diejenigen Werte, die später von der <code>reset</code>-Methode gesetzt werden,
| |
| können einfach mit <code>0</code> oder <code>null</code> vorbelegt werden.
| |
| | |
| ====Das Schläger-Objekt <code>g_paddle</code>====
| |
| Erweitern Sie das Schläger-Objekt <code>g_paddle</code> um eine Resetmethode, deren Aufgabe es ist, den Schläger
| |
| auf die Startposition (die ebenfalls in der Datei <code>CONSTANT.js</code> definiert wird)
| |
| und die Startgeschwindigkeit des Schlägers (in x-Richtung) auf <code>0</code> zu setzen:
| |
| | |
| <source lang="javascript">
| |
| // Moves the paddle to its starting position
| |
| // and sets its velocity to zero.
| |
| reset:
| |
| function()
| |
| { this.x = PADDLE_X;
| |
| this.y = PADDLE_Y;
| |
| this.vx = 0;
| |
| },
| |
| </source>
| |
| | |
| Legen Sie im Schläger-Objekt für alle [[Attribut]]e sinnvolle Initialwerte fest.
| |
| Diejenigen Werte, die später von der <code>reset</code>-Methode festgelegt werden,
| |
| können einfach mit <code>0</code> oder <code>null</code> vorbelegt werden.
| |
| | |
| Definieren Sie für das Schläger-Objekt ein weiteres Attribut <code>friction</code> und
| |
| initialisieren Sie dieses mit der Konstanten <code>PADDLE_FRICTION</code>.
| |
| Der Wert dieser Konstanten sollte ca. <code>0.3</code> betragen.
| |
| | |
| ===Die Draw-Funktion===
| |
| | |
| Die Draw-Funktion <code>f_draw</code> hat nur eine Aufgabe: Das Neuzeichnen der Bühne,
| |
| d.h., das Löschen des Bühneninhalts und das Neuzeichnen aller [[Sprites]]
| |
| auf der Bühne. Dazu ruft sie der Reihe nach die <code>draw</code>-Methoden der
| |
| einezelnen Sprites auf und übergibt diesen die Bühne auf der der jeweilige
| |
| Sprite gezeichnet werden soll. (In unseren Fall gibt es nur zwei Sprites: Ball und Schläger.)
| |
| | |
| <source lang="javascript">
| |
| // Clears the canvas and redraws all sprites (ball and paddle).
| |
| function f_draw()
| |
| { g_context.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
| |
| | |
| g_ball.draw(g_context);
| |
| g_paddle.draw(g_context);
| |
| }
| |
| </source>
| |
| | |
| Zusätzlich sollte man noch eine Funktion definieren, mit der man Informationen
| |
| in das Textfeld der HTML-Seite schreiben kann:
| |
| | |
| <source lang="javascript">
| |
| // Displays information on the HTML page.
| |
| function f_info(p_info)
| |
| { document.getElementById("d_info").firstChild.nodeValue = p_info; }
| |
| </source>
| |
| | |
| ===Die Init-Funktion===
| |
| | |
| Die Funktion <code>f_init</code> unterscheidet sich nicht wesentlich von der
| |
| Init-Funktion der beiden ersten Teile des Tutoriums.
| |
| | |
| Neu ist lediglich der Aufruf der Observer-Funktion <code>o_stop_game</code> und das Fehlen
| |
| der Timerdefinition. Der Animations-Timer wird im Spiel erst gestartet, wenn der Spieler
| |
| den Start-Knopf gedrückt hat. Die Stopp-Funktion hat die Aufgabe,
| |
| das Spiel in die Ausgangssituation zu bringen (siehe Definition dieser Funktion am Ende des Artikels).
| |
| Sie wird im weiteren Spielverlauf jedesmal dann aufgerufen, wenn ein Spiel durch einen Spielfehler
| |
| oder durch den Spieler beendet wird.
| |
| | |
| <source lang="javascript">
| |
| // Has to be called after the HTML page has been loaded.
| |
| function f_init()
| |
| { var l_canvas = document.getElementById("d_canvas");
| |
| | |
| // Initialize the canvas.
| |
| l_canvas.width = CANVAS_WIDTH;
| |
| l_canvas.height = CANVAS_HEIGHT;
| |
| g_context = l_canvas.getContext("2d");
| |
| | |
| // Reset the game (i.e. change its status to "waiting for game to be started").
| |
| o_stop_game();
| |
| }
| |
| | |
| // Execute the init function after the HTML page has been loaded.
| |
| window.onload = f_init;
| |
| </source>
| |
| | |
| ==Observer==
| |
| [[Observer]] sind speziele Funktionen, die in Falle von betimmten [[Ereignis (OOP)|Ereignis]]sen (events),
| |
| wie Timer-Event, Tasten-Event, Maus-Event etc. ausgeführt werden.
| |
| | |
| Die beiden Observer <code>o_start_paddle_moving</code> und <code>o_stop_paddle_moving</code>
| |
| können direkt aus der Datei <code>main.js</code> von Teil zwei des Tutoriums übernommen werden.
| |
| Sie haben die Aufgabe, den Schläger in eine bestimmte Richtung zu bewegen, solange der Spieler
| |
| eine bestimmte Taste drückt.
| |
| | |
| Der Observer <code>o_redraw</code> wächst im Teil 3 des Tutoriums zum „Moloch“ an. Er hat die Aufgabe,
| |
| eine Kollision zwischen Ball und Wand oder Ball und Schläger zu erkennen und entsprechend zu reagieren:
| |
| * Ball und Wand (links, rechts, oben): Ball prallt ab
| |
| * Ball und Wand (unten außerhalb des sichbaren Bereichs): Spielende
| |
| * Ball und Schläger: Ball prallt ab und wird evtl. abgelenkt; Erhöhung des Scores um einen Punkt
| |
| Außerdem muss diese Funktion auch noch den Canvas löschen und die neu positionierten
| |
| Sprites zeichnen, sofern die Funktion nicht aufgrund des Spielendes vorzeitig verlassen wurde.
| |
|
| |
| <source lang="javascript">
| |
| // An event observer:
| |
| // It is called every 1000/FPS milliseconds, but only when the game has been started.
| |
| function o_redraw()
| |
| { // Collision detection: ball <-> wall (left, right, top)
| |
| // Collision response: change the moving direction of the ball
| |
| if (g_ball.x <= g_ball.r || g_ball.x >= CANVAS_WIDTH - g_ball.r)
| |
| { g_ball.vx = -g_ball.vx; }
| |
| if (g_ball.y <= g_ball.r)
| |
| { g_ball.vy = -g_ball.vy; }
| |
| | |
| // Collision detection: ball <-> wall (bottom outside of the canvas)
| |
| // Collision response: stop the game (as the ball has vanished)
| |
| if (g_ball.y - 2*g_ball.r > CANVAS_HEIGHT)
| |
| { o_stop_game();
| |
| return; // leave o_redraw early
| |
| }
| |
|
| |
| // Collision detection: ball <-> paddle
| |
| // Collision response: change the moving direction of the ball
| |
| // + change the velocity of the ball
| |
| // (due to the friction between the ball and the paddle)
| |
| // + display the new score
| |
| // Collision detection and handling has to be improved:
| |
| // A collision with the small side of the paddle should be handled.
| |
| if ( g_ball.y + g_ball.r >= g_paddle.y
| |
| && g_ball.y + g_ball.r <= g_paddle.y + g_paddle.height
| |
| && g_ball.x + 0.5*g_ball.r >= g_paddle.x
| |
| && g_ball.x - 0.5*g_ball.r <= g_paddle.x + g_paddle.width
| |
| )
| |
| { // Resolve penetration.
| |
| if (g_ball.vy > 0) // The ball is moving from top to bottom.
| |
| { g_ball.y = g_paddle.y - g_ball.r; }
| |
| else // The ball is moving from bottom to top.
| |
| { g_ball.y = g_paddle.y + g_paddle.height + g_ball.r; }
| |
|
| |
| g_ball.vy = -g_ball.vy;
| |
| g_ball.vx += g_paddle.friction*g_paddle.vx;
| |
|
| |
| // Raise the score and display it.
| |
| g_score++;
| |
| f_info("Punkte: " + g_score);
| |
| }
| |
|
| |
| // Move the ball.
| |
| g_ball.move();
| |
|
| |
| // Move the paddle if possible.
| |
| if ( (g_paddle.vx < 0 && g_paddle.x > 0)
| |
| || (g_paddle.vx > 0 && g_paddle.x + g_paddle.width < CANVAS_WIDTH)
| |
| )
| |
| { g_paddle.move(); }
| |
| | |
| // Redraw the canvas.
| |
| f_draw();
| |
| }
| |
| </source>
| |
| | |
| Im Spiel gibt es noch zwei weitere Observer. Der Observer
| |
| <code>o_start_game</code> wird bei einem Klick auf den
| |
| Start/Stopp-Button aufgerufen, falls diese als Start-Button
| |
| fungiert. Seine Aufgabe ist es, den Score auf Null zurückzusetzen,
| |
| den Start/Stopp-Button als Stopp-Button zu aktivieren, die
| |
| Steuerung des Schlägers zu aktivieren, den Animations-Timer der „physics engine“
| |
| zu starten und zu guter Letzt den eine Information über den
| |
| Spielstart in das Textfeld auf der Bühne zu schreiben. Aktuell könnte
| |
| im letzten Schritt auch gleich der aktuelle Punktestand (nnull Punkte)
| |
| angezeigt werden.
| |
| | |
| <source lang="javascript">
| |
| // An event observer:
| |
| // It is called whenever a "start game event" is signaled.
| |
| function o_start_game()
| |
| { // Reset the score.
| |
| g_score = 0;
| |
| | |
| // Set the button "d_start_stop" to be a stop button (view and behaviour).
| |
| document.getElementById("d_start_stop").value = "Stopp";
| |
| document.getElementById("d_start_stop").onmousedown = o_stop_game;
| |
| | |
| // React to key down and key up events.
| |
| document.onkeydown = o_start_paddle_moving;
| |
| document.onkeyup = o_stop_paddle_moving;
| |
| | |
| // Start the timer for redrawing the canvas every 1000/FPS milliseconds.
| |
| g_timer = window.setInterval(o_redraw, 1000/FPS);
| |
| | |
| // Show the current score (zero points)
| |
| f_info("Punkte: " + g_score);
| |
| }
| |
| </source>
| |
| | |
| Beachten Sie, dass der Code <code>f_info("Punkte: " + g_score);</code>
| |
| an zwei Stellen im Programm aufgerufen wird. Damit ist der Code nicht mehr
| |
| [[Don't repeat yourself|DRY]]. Wenn die Anzeige der Punkte veränder werden soll,
| |
| muss dies an zwei Stellen erfolgen. Wie kann man den Code verbessern?
| |
| | |
| Die Aufgabe des Stopp-Spiel-Observers ist im Wesentlichen,
| |
| die Aktionen des Start-Spiel-Servers rückgängig zu machen:
| |
| * Der Animations-Timer wird gestoppt.
| |
| * Die Schläger-Steuerungstasten werden deaktiviert.
| |
| * Der Start-Stop-Button wird als Start-Button aktiviert.
| |
| | |
| Außerdem werden der Ball un der Schläger an ihre jeweiligen Startpositionen gesetzt.
| |
| Anschließend muss die Bühne neu gezeichnet werden, damit die neuen Positionen dieser
| |
| biden Sprites auch sichtbar wird.
| |
| | |
| <source lang="javascript">
| |
| // An event observer:
| |
| // It is called whenever a "stop game event" is signaled.
| |
| function o_stop_game()
| |
| { // Stop the canvas redrawing timer.
| |
| window.clearInterval(g_timer);
| |
| | |
| // Stop reacting to key down and key up events.
| |
| document.onkeydown = null;
| |
| document.onkeyup = null;
| |
| | |
| //Set the button "d_start_stop" to be a start button (view and behaviour).
| |
| document.getElementById("d_start_stop").value = "Start";
| |
| document.getElementById("d_start_stop").onmousedown = o_start_game;
| |
| | |
| // Put the ball and the paddle on their starting positions
| |
| // and then redraw the canvas.
| |
| g_ball.reset();
| |
| g_paddle.reset();
| |
| f_draw();
| |
| }
| |
| </source>
| |
| | |
| Man beachte, dass die Befehle in umgekehrter Reihenfolge wie in der Start-Funktion
| |
| ausgeführt werden. Dies wird bei komplementären Start-/Stop-Anweisungen üblicherweise so gemacht,
| |
| da es bei dieser Reihenfolge der Stop-Anweisungen i. Allg. nicht zu Abhängigkeitsfehlern kommt.
| |
| Wenn beispielsweise die Anweisung B nur dann funktioniert, wenn das Ergebnis der
| |
| Anweisung A vorliegt, dann muss A vor B gestartet aber nach B beendet werden.
| |
| | |
| Man beachte außerdem, dass die Stop-Funktion den Score nicht verändert, damit der Spieler
| |
| die erzielten Punkte auch nach Spielende noch lesen kann. Hier ist allerdings noch ein kleiner Fehler
| |
| enthalten.
| |
| | |
| ==Anwendung testen und sichern==
| |
| Testen Sie Ihre Anwendung in gewohnter Weise.
| |
| | |
| Vergessen Sie nicht, sie im SVN-Repository zu sichern.
| |
| | |
| =Erweiterung=
| |
| | |
| Erweitern Sie die Anwendung so, dass Sie zwei Schläger unabhängig voneinander
| |
| links und rechts am Bühnenrand bewegen können, d.h., dass zwei Spieler gegeneinander spielen können.
| |
| Jeder Spieler hat seinen eigenen Score. Der erste Spieler, der 7 Punkte erreicht, gewinnt.
| |
| | |
| Am besten zeigen Sie die erzielten Punkte auch auf der Bühne selbst an (vgl. Bilder im [[Wikipedia:Pong|Wikipedia-Artikel zu Pong]]).
| |
| Wie man Text auf der Bühne darstellen kann, wurde im [[HTML5-Tutorium: Canvas: Hello World|Hello-World-Tutorium]] gezeigt.
| |
| | |
| [[Datei:MiniPong03aCanvas.png|miniatur|ohne|945px|Das Objektmodell von MiniPong 03a]]
| |
| | |
| Beachten Sie wieder das [[Programmierprinzip#Don.27t_repeat_yourself.2C_DRY.5B4.5D|Programmierprinzip „Don't repeat yourself“ (Dry)]].
| |
| | |
| '''Musterlösung''': <code>[http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03a/WebContent/index.html Minipong 3a]</code>
| |
| ([http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03a SVN-Repository])
| |
| | |
| '''Siehe auch''': http://www.guimp.com/pong_flash.html; Hier ist der Name MiniPong wirklich gerechtfertigt.
| |
| | |
| =Probleme der Kollisionserkennung=
| |
| | |
| Die in [[HTML5-Tutorium: Canvas: MiniPong 01#Probleme_der_Kollisionserkennung|Teil 1 des Tutoriums angesprochenen]] Probleme de „Tunnelns“ und der „Überlappung“
| |
| bei einer [[Kollisionserkennung und -behandlung#A-posteriori-Kollisionserkennung_.28auch_.E2.80.9Ediskrete_Kollisionserkennung.E2.80.9C.29|A-posteriori-Kollissionserkennung]] kann man in dieser Minipong-Version besonders schön veranschaulichen.
| |
| | |
| ==Tunneln==
| |
| Beispiel: [http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03_tunneling/WebContent/index.html modifizierte Minipong-Anwendung]
| |
| | |
| In diesem Beispiel wurden einfach die Framrate deutlich reduziert (auf 10 Frames pro Sekunde)
| |
| sowie eine hohe Ballgeschwindigkeit (200 Pixel pro Sekunde in y-Richtung) eingestellt.
| |
| | |
| (Damit Sie den Effekt auf jeden Fall beobachten können, wurde außerdem die Steuerung des
| |
| Schlägers deaktiviert.)
| |
| | |
| ==Überlappung==
| |
| Beispiele: [http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03_penetration/WebContent/index.html modifizierte Minipong-Anwendung],
| |
| [http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03a_penetration/WebContent/index.html modifizierte Pong-Anwendung],
| |
| | |
| Um diesen Effekt zu erreichen wurde nur eine wesentliche Änderung am Source-Code vorgenommen.
| |
| In der Funktion <code>o_redraw</code> wurden folgende fünf Zeilen auskommentiert:
| |
| | |
| <source lang="javascript">
| |
| // Resolve penetration.
| |
| if (g_ball.vy > 0) // The ball is moving from top to bottom.
| |
| g_ball.y = g_paddle.y - g_ball.r;
| |
| else // The ball is moving from bottom to top.
| |
| g_ball.y = g_paddle.y + g_paddle.height + g_ball.r;
| |
| </source>
| |
| | |
| Diese Anweisungen verschieben den Ball im Falle einer Kollision soweit, dass er den Schläger nicht überlappt, sondern nur berührt.
| |
| Wenn diese Korrekturmaßnahme unterbleibt, kann der Ball in gewissen Situationen im Schläger hängen beleiben. Eine derartige Situation kann man beispielsweise erzwingen,
| |
| wenn man in der Datei <code>CONSTANT.js</code> der Minipong-Anwendung folgende Werte wählt:
| |
| | |
| <source lang="javascript">
| |
| BALL_VX_MIN = 116, // pixels per second
| |
| BALL_VX_MAX = 116, // pixels per second
| |
| BALL_VY_MIN = 21, // pixels per second
| |
| BALL_VY_MAX = 21, // pixels per second
| |
| </source>
| |
| | |
| Aber auch ohne Modifikation der Startwerte des Balls kann dieser Effekt auftreten,
| |
| wenn man den Ball während des Spiels geeignet mit dem Schläger ablenkt.
| |
| | |
| =Weitere Verbesserungsmöglichkeiten=
| |
| | |
| Der hier vorgestellte Minipong-Code realisiert alle in der Use-Case-Beschreibung geforderten Anwendungsfälle:
| |
| | |
| [[Datei:MiniPongUseCases.png|miniatur|ohne|924px|Use Cases der Tutoriums-Anwendung <code>Minipong</code>]]
| |
| | |
| Der Code ist dennoch nicht zufriedenstellend, da viele [[Programmierprinzipien]] verletzt werden.
| |
| Auf die Beachtung dieser Prinzipien könnte bei Mini-Anwendungen wie MiniPong sicherlich verzichtet werden,
| |
| da der Aufwand größer ist als der Ertrag.
| |
| Bei größeren Anwendungen sollte man dies Prinzipien allerdings tunlichst beachten, da man anderenfalls mit großer Wahrscheinlichkeit
| |
| keine robust laufende Anwendung und keinen wiederverwendbaren Code erhält.
| |
| | |
| {{Codequalität
| |
| | application = MiniPongCanvas03
| |
| | readability = 4
| |
| | writability = 3
| |
| | continuity = 4
| |
| | customizability = 4
| |
| | dry = 3
| |
| | demeter = 5
| |
| | verifiability = 2
| |
| | interfaces = 5
| |
| | contract = 0
| |
| | liskov = 5
| |
| | modularity = 1
| |
| }}
| |
| | |
| ==Verstöße gegen das „[[Programmierprinzipien#Verst.C3.A4ndlichkeit.2C_Comprehesibility.2C_Lesbarkeit.2C_Readability|Prinzip der Verständlichkeit/Lesbarkeit]]“==
| |
| | |
| Es gibt nur einen wesentlichen Verstoß gegen das Prinzip „Verständlichkeit/Lesbarkeit“:
| |
| * '''Die Kommentare wurden nicht im [[JSDoc]]-Format geschrieben.'''
| |
| Dieses Format kann verwendet werden, um eine [[API]]-Dokumentation automatisch zu generieren.
| |
| | |
| Ansonsten wurde versucht, den Code so lesbar und verständlich wie möglich zu gestalten:
| |
| | |
| * Die Breite des Codes wurde fast überall auch 80 Zeichen beschränkt, damit er auf normalem Dia-A4-Papier in normaler Schriftgröße ausgedruckt werden kann.
| |
| * Der Code wurde sauber und platzsparend eingerückt.
| |
| * Klammerpaare werden leicht als solche erkannt.
| |
| * Es gibt keine überflüssigen Leerzeilen.
| |
| * Es gibt keine Tab-Symbole, damit der Code auch mit anderen Text-Programmen (wie z.B. in einem Browser) gut gelesen werden kann.
| |
| * Die Bezeichner von Variablen, Funktionen etc. unterscheiden sich syntaktisch so, dass man ihre Aufgabe sofort erkennt. Beispielsweise handelt es sich bei <code>g_ball</code> um eine globale Variable, bei <code>p_event</code> um einen [[Parameter]] und bei <code>CANVAS_WIDTH</code> um eine Konstante (vgl. [[Multimedia-Programmierung: Style Guide|Style Guide]]).
| |
| * Die Namen der Variablen, Parameter, Funktionen, Attribute, Methoden und Konstanten sind selbsterklärend. Beispielsweise wird in der Variablen <code>g_ball</code> ein Ballobjekt gespeichert.
| |
| * Der Code wurde mit sinnvollen Kommentaren versehen (für fortgeschrittene Programmierer sollte die Zahl allerdings etwas reduziert werden).
| |
| | |
| ==Verstöße gegen das „[[Programmierprinzipien#Schreibbarkeit.2C_Writability|Prinzip der Schreibbarkeit]]“==
| |
| | |
| '''Die Entwicklungsumgebung ist in mancherlei Hinsicht nicht zufriedenstellend:'''
| |
| | |
| * Juno ist deutlich träger als Indigo. Diese Erfahrung haben neben [[Wolfgang Kowarschick]] anscheinend mehrere Benutzer gemacht (siehe z.B. [https://bugs.eclipse.org/bugs/show_bug.cgi?id=385272], [http://www.eclipse.org/forums/index.php/m/901301/], [http://stackoverflow.com/questions/11639108/eclipse-juno-wtp-egit-dead-slow]).
| |
| * Juno ist anscheinend auch hinsichtlich der Fehlerkorrektur und der Autovervollständigung ein Rückschritt gegenüber Indigo (eigene Erfahrung von [[Wolfgang Kowarschick]]).
| |
| | |
| Zur Lösung dieser Probleme sollte man versuchsweise auf Indigo umsteigen (auch wenn auf [http://www.eclipse.org/downloads/ http://www.eclipse.org/] Juno defaultmäßig zum Download angeboten wird).
| |
| Eine Alternative wäre evtl. auch die Entwicklungsumgebung [http://netbeans.org/ Netbeans] mit der ich ([[Wolfgang Kowarschick]]) sehr gute Erfahrungen hinsichtlich der Entwicklung Web-Anwendungen gemacht habe.
| |
| | |
| Aber selbst wenn man diejenige Entwicklungsumgebung gefunden hat, mit der man persönlich am Besten zurechtkommt, gibt es
| |
| weitere Verbesserungsmöglichkeiten:
| |
| | |
| * Für Code-Schnipsel, die man häufig verwendet, sollte man Tastaturkürzel definieren. Ein sehr interessantes Werkzeug für diesen Zweck ist [http://code.google.com/p/zen-coding/ Zen Coding] (siehe auch [http://nooshu.com/aptana-on-steroids-using-zen-coding Aptana on Steroids using Zen Coding] und [https://github.com/sergeche/eclipse-zencoding eclipse-zencoding]).
| |
| * Für Dateien (html, js, css ...) sollte man sich Templates anlegen bzw. die Templates der verwendeten Entwicklungsumgebung an seine Vorlieben anpassen.
| |
| * Das Grundgerüst des zu implementierenden Codes (Objekt-, Klassen-, Attribut-, Methoden-Definitionen etc.) sollte automatisch generiert werden (z.B. mit Hilfe eines UML-Werkzeugs).
| |
| * Für Code und Modell gemeinsam sollte [[Refactoring]] verfügbar sein. Das heißt, es sollte jederzeit problemlos möglich sein, die Namen von Klassen, Objekten, Methoden, Attributen etc. konsistent in allen Dateien zu ändern.
| |
| | |
| ==Verstöße gegen das „[[Programmierprinzipien#Stetigkeit.2C_Continuity|Prinzip der Stetigkeit]]“==
| |
| | |
| Ein Programm-Code heißt [[Programmierprinzipien#Stetigkeit.2C_Continuity|stetig]],
| |
| wenn kleine Änderungswünsche auch nur kleine Änderungen am Code zur Folge haben.
| |
| | |
| Dieses Prinzip wird relativ gut beachtet. Man vergleiche beispielsweise den Code
| |
| der MiniPong-Datei [http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03/WebContent/js/main.js <code>main.js</code>]
| |
| der Canvas-Version mit dem Code von [http://glossar.hs-augsburg.de/beispiel/tutorium/html_svg/minipong/html_svg_minipong_03/WebContent/js/main.js <code>main.js</code>]
| |
| der SVG-Version:
| |
| # Beide Musterlösungen ([http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03/ Canvas-Version], [http://glossar.hs-augsburg.de/beispiel/tutorium/html_svg/minipong/html_svg_minipong_03/ SVG-Version]) in Eclipse laden
| |
| # Im Projektexplorer: beide <code>main.js</code>-Dateien selektieren
| |
| # Rechtsklick auf eine der beiden Dateien → <code>Compare with</code> → <code>Einander</code>
| |
| | |
| Der Änderungswunsch lautete „Ersetze die Canvas-Bühne durch eine SVG-Bühne“. Der in zwei Tutorien
| |
| ([[HTML5-Tutorium: Canvas: MiniPong 03]] und [[HTML-Tutorium: SVG: MiniPong 03]]) erstellte Code unterscheidet sich
| |
| nicht sonderlich stark.
| |
| | |
| Hinsichtlich des Prinzips „Don't repeat yourself“ sind allerdings noch einige Verbesserungen möglich (siehe unten).
| |
| | |
| ==Verstöße gegen das „[[Programmierprinzipien#Konfigurierbarkeit.2C_Customizability|Prinzip der Konfigurierbarkeit]]“==
| |
| | |
| Ein wichtiges Prinzip, um Stetigkeit zu erzielen, ist, die einfache Konfigurirbarkeit der Anwendung sicherzustellen.
| |
| Konstanten sollten nie im Code stehen, sondern immer in einer eigenen Konfigurationsdatei.
| |
| | |
| Gegen dieses Prinzip wird nur in einer Hinsicht verstoßen:
| |
|
| |
| '''Texte, mit denen der Benutzer informiert wird (<code>Start</code>, <code>Stopp</code>, <code>Punkte</code>) stehen direkt im Code.'''
| |
| | |
| (Anmerkung: In der Datei [http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03a/WebContent/js/main.js <code>main.js</code>]
| |
| der zu [http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03a/ Pong erweiterten Version von MiniPong] sind noch deutlich mehr
| |
| Informationstexte direkt im Code enthalten.)
| |
| | |
| Alle anderen Konstanten wurden in die Datei <code>CONSTANT.js</code> ausgelagert, die somit für MiniPong als Konfigurationsdatei fungiert.
| |
| | |
| In eine künftigen MiniPong-Version sollten die Informationstexte auch in eine Informationsdatei ausgelagert werden.
| |
| Im Rahmen dieser Auslagerung sollte MiniPong gleich „[[Internationalisierung|internationalisiert]]“ werden. Das heißt, die Informationstexte
| |
| sollten jeweils in der Sprache von Benutzer bevorzugten Sprache ausgeliefert werden.
| |
| | |
| ==Verstöße gegen das Prinzip „[[Don't repeat yourself|Don't repeat yourself]]“==
| |
| | |
| Die Beactung des Prinzips „Don't repeat yourself“ (DRY) ist ein weiteres wichtiges Instrument,
| |
| um die Stetigkeit von Programmcode zu erhöhen.
| |
| | |
| '''Der Minipong-Code ist nicht [[Don't repeat yourself|DRY]].'''
| |
| | |
| Beispiel: Der Code <code>f_info("Punkte: " + g_score);</code> wird
| |
| an zwei Stellen im Programm aufgerufen. Wenn irgendwann einmal
| |
| der Text der Punktausgabe verändert werden soll, muss dies an zwei Stellen erfolgen.
| |
| Ändert man den Text nur an einer Stelle, so verhält sich das Programm
| |
| danach inkonsitstent.
| |
| | |
| Es gibt aber noch eine subtilere Form der Wiederholung von Code:
| |
| Die grafischen Objekte (Ball, Schläger) enthalten
| |
| gleiche Attribute (z.B. Position <code>x</code>, <code>y</code>) und
| |
| gleiche Methoden (<code>move</code>, <code>draw</code>) mit vergleichbaren Aufgaben.
| |
| Die Methode <code>move</code> könnte beispielsweise für alle grafischen
| |
| Objekte identisch definiert werden:
| |
| | |
| <source lang="javascript">
| |
| move:
| |
| function()
| |
| { this.x += this.vx/FPS;
| |
| this.y += this.vy/FPS;
| |
| this.vx += this.ax * (this.vx > 0 ? 1 : -1);
| |
| this.vy += this.ay * (this.vy > 0 ? 1 : -1);
| |
| },
| |
| </source>
| |
| | |
| Voraussetzung wäre allerdings, dass alle grafischen Objekte je einen Positionsvektor
| |
| $p = (x,y)$, einen Geschwindigkeitsvektor $v = (v_x, v_y)$ sowie einen Beschleunigungsvektor $a = (a_x, a_y)$
| |
| besitzen würden.
| |
| | |
| Selbst die obige Definition der Funktion <code>move</code> ist noch nicht DRY. Beispielsweise sollte
| |
| man an Stelle von
| |
| <source lang="javascript">
| |
| this.x += this.vx/FPS;
| |
| this.y += this.vy/FPS;
| |
| </source>
| |
| besser mit echten 2D-Vektor-Objekten (wie z.B. <code>this.position</code> und <code>this.velocity</code>) arbeiten,
| |
| für die es Methoden wie <code>modifyAdd</code> und <code>scalar</code> gibt:
| |
| | |
| * <code>modifyAdd</code>: Modifiziert den aktuellen Vektor, indem sie einen zweiten Vektor hinzuaddiert.
| |
| * <code>scalar</code>: Berechnet ein Skalarprodukt, d.h multipliziert den aktuellen Vektor mit einer reelen Zahl, modifiziert aber den aktuellen Vektor nicht.
| |
| | |
| Die neue Position eines grafischen Objektes würde dann folgendermaßen ermittelt werden:
| |
| | |
| <source lang="javascript">
| |
| this.position.modifyAdd(this.velocity.scalar(1/FPS));
| |
| </source>
| |
| | |
| ==Verstöße gegen das „[[Programmierprinzipien#Gesetz_von_Demeter.5B5.5D.2C_Law_of_Demeter.2C_LoD|Gesetz von Demeter]]“==
| |
| | |
| Das [[Programmierprinzipien#Gesetz_von_Demeter.5B5.5D.2C_Law_of_Demeter.2C_LoD|Gesetz von Demeter]] verlangt, dass es keine Ketten von Methoden- und/oder
| |
| Attributaufrufen gibt, dass ein Objekt also nicht mit „Freunden von Freunden“ kommuniziert, sondern nur mit seinen direkten „Freunden“. Anweisungen der Art <code>object.attrtibut.methode()</code>, in der zwei oder mehr Punktoperationen ($\cdots$<code>.</code>$\cdots$) vorkommen,
| |
| sind verboten. (Ein führendes <code>this.</code> ist allerdings erlaubt.)
| |
| | |
| Die Beachtung des Gesetes von Demeter erhöht die Stetigkeit.
| |
| | |
| '''Gegen dieses Gesetz wird in <code>MiniPongCanvas03</code> nicht verstoßen.'''
| |
| | |
| In der SVG-Version [http://glossar.hs-augsburg.de/beispiel/tutorium/html_svg/minipong/html_svg_minipong_03/WebContent/js/main.js <code>MiniPongSVG03</code>] kommt
| |
| derartiger Code allerdings in der <code>init</code>-Methode des Ball-Objektes vor:
| |
| | |
| <source lang="javascript">
| |
| p_canvas
| |
| .circle(this.x, this.y, this.r)
| |
| .attr({"stroke" : BALL_BORDER_COLOR,
| |
| "stroke-width" : BALL_BORDER_WIDTH,
| |
| "fill" : BALL_COLOR,
| |
| });
| |
| </source>
| |
| | |
| In diesen speziellen Fall liegt allerdings kein Verstoß gegen das Gesetz von Demeter vor.
| |
| | |
| Die Methode <code>circle</code> fungiert als Konstruktor. Sie erzeugt ein neues Objekt
| |
| und liefert das neu erzeugte Objekt als Ergebnis. Auf dieses Objekt wird die Methode <code>attr</code>
| |
| angewendet. Der obige Code könnte also auch – etwas umständlicher – folgendermaßen geschrieben werden:
| |
| | |
| <source lang="javascript">
| |
| var l_circle = p_canvas.circle(this.x, this.y, this.r);
| |
| | |
| l_circle.attr({"stroke" : BALL_BORDER_COLOR,
| |
| "stroke-width" : BALL_BORDER_WIDTH,
| |
| "fill" : BALL_COLOR,
| |
| });
| |
| </source>
| |
| | |
| Das heißt, bei beiden der obigen Methodenaufrufen „spricht“ das Ball-Objekt nur, wie von Demeter gefordert,
| |
| mit einem ihm direkt bekannten „Freund“: <code>p_canvas</code> wurde ihm als Parameter übergeben und <code>l_circle</code>
| |
| hat es selbst erzeugt.
| |
| | |
| ==Verstöße gegen das „[[Programmierprinzipien#.C3.9Cberpr.C3.BCfbarkeit.2C_Verifiability|Prinzip der Überprüfbarkeit]]“==
| |
| | |
| '''Bis auf ein einfaches Use-Cases-Diagramm und ein Objektmodell gibt es derzeit keine formale Spezifikation der Anwendung.'''
| |
| | |
| Das heißt: Derzeit kann formal nicht beweisen werden, dass die Anwendung das tut, was von ihr erwartet wird.
| |
| Es gibt auch keine Testfälle, mit denen die Erfüllung der Spezifikation zumindest teilweise verifiziert werden könnte.
| |
| Es bleibt dem Entwickler nur, Beta-Tester zu suchen, die das Spiel ausführlich auf diversen Brwosern testen und
| |
| ihm gefundene Probleme mitteilen. (Er selbst kann auch als Beta-Tester fungieren.)
| |
| | |
| Es fehlen:
| |
| | |
| * Ein gutes ([[UML]]-)[[Datenmodell]], das möglichst viele Aspekte der guten Programmierung umsetzt.
| |
| * Ein Code-Generator, der das Datenmodell automatisch in ein Codegerüst überführt.
| |
| * Eine formale Spezifikation der Aufgaben der Funktionen und Methoden oder zumindest eine hinreichende Anzahl an Testfällen ([[unit test]]s), mit der die [[Semantik]] der Funktionen und Methoden beschrieben wird.
| |
| * Ein Testwerkzeug, wie z.B. [http://busterjs.org Buster,js], um zu verifizieren, ob der aktuelle Code tatsächlich die mit Hilfe von Testfällen spezifizierte Semantik realisiert. (Siehe auch [[WikipediaEN:List_of_unit_testing_frameworks#JavaScript|Liste von Unit-Werkzeugen auf Wikipedia]])
| |
| | |
| ==Verstöße gegen das Prinzip „[[Programmierprinzipien#Benutze_Schnittstellen.2C_Make_Use_of_Interfaces|Benzutze Interfaces]]“==
| |
| In JavaScript gibt es keine [[Klasse]]n und damit erst recht keine [[Schnittstelle_(OOP)|Interfaces]] (= Klasse ohne Implementierung der Methoden).
| |
| Daher kann dieses Prinzip in JavaScript-Code höchstens auf Spezifikationsebene (d.h. in Objekt- und Klassendiagrammen),
| |
| aber nicht im Code selbst beachtet werden.
| |
| | |
| ==Verstöße gegen das Prinzip „[[Programmierprinzipien#Benutze_Integrit.C3.A4tsbedingungen.2C_Make_Use_of_Integrity_Constraints.2C_Design_by_Contract.5B2.5D|Design by Contract]]“==
| |
| | |
| '''Da es für die MiniPong-Anwendung keinerlei formale Spezifikation gibt (zu der auch die Definition von Integritätsbedigungen zählt), verletzt diese Anwendung das Prinzip „Design by Contract“ zu 100 Prozent.'''
| |
| | |
| Das Prinzip „Design by Contract“ wurde von Bertrand Meyer formuliert.<ref name="Meyer">{{Quelle|Meyer, B. (1997): Object-oriented Software Construction}}</ref>
| |
| Es besagt im Wesentlichen, dass jede [[Operation]] ([[Funktion]], [[Prozedur]], [[Methode]] ...) einen „Vertrag“ zu erfüllen hat: Wenn der Aufrufer sicherstellt, dass die
| |
| so genannten [[Vorbedingung]]en erfüllt sind, führt die Operation ihre Aufgabe ordnungsgemäß aus. Das heißt, sie garantiert, dass durch die
| |
| Ausführung des zugehörigen Codes keine [[Invariante]]n verletzt werden und dass nach Abschluss der Operation alle [[Nachbedingung]]en erfüllt sind.
| |
| | |
| Für jede Operation müssen also so genannte [[Integritätsbedingung]]en definiert werden:
| |
| | |
| * [[Vorbedingung]]en müssen erfüllt sein, bevor eine Operation ausgeführt werden kann.
| |
| * [[Nachbedingung]]en müssen erfüllt sein, nachdem eine Operation ausgeführt wurde.
| |
| * [[Invariante]]n müssten stets erfüllt sein.
| |
| | |
| Bei JavaScript gibt es noch nicht einmal die einfachste Form der Integritätsbedingungen, die [[Typisierung]] von Variablen und Parametern.
| |
| Jede Variable und jeder Parameter kann zu jedem Zeitpunkt einen [[Wert]] oder ein [[Objekt]] von einem anderen Typ haben.
| |
| Es ist z.B. jederzeit möglich, der Variablen <code>x</code> des Ball-Objekts eine Zeichenkette als Wert zuzuweisen und
| |
| damit einen Laufzeitfehler zu provozieren:
| |
| | |
| <source lang="javascript">
| |
| g_ball.x = "ganz links am Bildschirmrand";
| |
| </source>
| |
| | |
| Um dieses Problem abzumildern, muss man entweder
| |
| | |
| * geeignete Sicherheitsabfragen (z.B. mit Hilfe von Assertbefehlen wie <code>assert(isInteger(x))</code>) in den Code einfügen oder
| |
| * zahlreiche zusätzliche Testfälle generieren, die in einer typisierten Sprache überflüssig wären, oder
| |
| * ein Werkzeug verwenden, dass die Korrektheit einer JavaScript-Anwendung automatisch beweist oder widerlegt.
| |
| | |
| Die erste Lösung hat zwei gravierende Nachteile:
| |
|
| |
| * Fehler werden erst zur Laufzeit und nicht zur Entwicklungszeit (Compilezeit) entdeckt.
| |
| * Assert-Befehle verlangsamen den Code, während Integritätsbedingungen, die von einem Compiler zur Compilezeit berücksichtigt werden, den Code sogar beschleunigen können.
| |
| | |
| Die zweite Lösung ist machbar, aber ziemlich aufwändig, wenn man alle Testfälle „von Hand“ generieren muss.
| |
| | |
| Die dritte Lösung wäre wunderbar geeignet, aber leider gibt es keine bislang keine derartigen Werkzeuge für JavaScript. Zumindest ist mir ([[Wolfgang Kowarschick]]) keines bekannt.
| |
| | |
| Für die erste Lösung des Problems bietet sich eine Kompromisslösung an. Man versieht den Code mit Assertbefehlen und anderen Integritätstests
| |
| und lässt diese später beim Komprimieren des JavaScript-Codes automatisch entfernen. So bekommt man zum Entwicklungszeitpunkt zumindest bei
| |
| Laufzeittests aussagekräftige Fehlermeldungen, hat aber im Produktivbetrieb keine Leistungseinbußen.
| |
| | |
| ==Verstöße gegen das „[[Programmierprinzipien#Liskovsches_Substitutionsprinzip.5B3.5D.2C_LSP.2C_Ersetzbarkeitsprinzip.2C_Liskov_substitution_principle.5B4.5D|Liskovsches Substitutionsprinzip]]“==
| |
| | |
| Das „Liskovsche Substitutionsprinzip“ besagt, dass man eine geerbte Methode nicht so überschreiben darf, dass sich
| |
| das Objekt überraschend anders verhält, als es es täte, wenn die Methode nicht überschrieben worden wäre.
| |
| | |
| Da Vererbung in MiniPong gar nicht verwendet wird, wird gegen dieses Prinzip auch nicht verstoßen.
| |
| | |
| ==Verstöße gegen das „[[Programmierprinzipien#Modularit.C3.A4t.2C_Modularity.2C_Teile_und_herrsche.2C_Divide_et_impera|Prinzip der Modularität]]“==
| |
| '''<code>MiniPongCanvas03</code> wurde als „Moloch“ realisiert'''. Von Modularisierung ist allenfalls am Rande etwas zu merken:
| |
| | |
| * Die Konstanten wurden in eine eigene Datei ausgelagert (aber liegen immer noch direkt im ROOT-Objekt der Anwendung).
| |
| * Die Ball- und Paddle-Methoden wurden in die das Ball- und das Schläger-Objekt integriert.
| |
| | |
| Hier besteht daher noch großes Verbesserungspotenzial. Es gibt immer noch viele zu viele globale Variablen (<code>g_...</code>) und zu viele globale Funktionen
| |
| (<code>f_...</code> sowie alle Observer-Funktionen). Bei sauberer Modularisierung des Codes lösen sich viele der zuvor beschriebenen
| |
| Probleme in Wohlgefallen auf.
| |
| | |
| '''Anmerkung:''' Verglichen mit einer früheren Version von <code>MiniPongCanvas03</code> ist der aktuelle Code allerdings schon richtig modular.
| |
| Man sehe sich mal
| |
| [http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03_moloch/WebContent/js/main.js <code>main.js</code>]
| |
| der [http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_03_moloch/ Vorgänger-Version vom Wintersemester 2011] an.
| |
| Hier ist sämlicher Code in einer Datei enthalten (die Konstanten sind im Code verteilt), ein Ball-Objekt und ein Schläger-Objekt gibt es auch noch nicht.
| |
| | |
| =Quellen=
| |
| <references/>
| |
| <ol start = "2">
| |
| <li>{{Quelle|Braun, H. (2011): Webanimationen mit Canvas}}</li>
| |
| <li>{{Quelle|Kowarschick, W.: Multimedia-Programmierung}}</li>
| |
| </ol>
| |
| | |
| =Fortsetzung des Tutoriums=
| |
|
| |
|
| Sie sollten nun [[HTML5-Tutorium: Canvas: MiniPong 04|Teil 4 des Tutoriums]] bearbeiten. | | Kopieren Sie die Dateien des Projektes [https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong03/ MiniPong03] |
| | in das neue Projekt. |
|
| |
|
| | ==Quellen== |
| | # {{Quelle|Braun, H. (2011): Webanimationen mit Canvas}} |
| | # {{Quelle|Kowarschick, W.: Multimedia-Programmierung}} |
| [[Kategorie: HTML5-Tutorium: Canvas: MiniPong]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]] | | [[Kategorie: HTML5-Tutorium: Canvas: MiniPong]][[Kategorie: HTML5-Beispiel]][[Kategorie:Kapitel:Multimedia-Programmierung:Beispiele]] |