HTML5-Tutorium: Canvas: MiniPong 05: Unterschied zwischen den Versionen

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg
Keine Bearbeitungszusammenfassung
 
(67 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
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_04/WebContent/index.html Minipong 4]</code>
'''Musterlösung''': <code>[https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong05/web/index.html index.html]</code>
([http://glossar.hs-augsburg.de/beispiel/tutorium/html5_canvas/minipong/html5_canvas_minipong_04 SVN-Repository])
([https://glossar.hs-augsburg.de/beispiel/tutorium/es5/minipong/WK_MiniPong05/ WK_MiniPong05 (SVN)])


{{In Bearbeitung}}
==Ziel: Das fertige Spiel „MiniPong“ verbessern==
=Ziel: Simulation von Klassen in JavaScript=
Im fünften Teil des Tutoriums wird beschrieben, wie die funktionsfähige Version von MiniPong verbessert und erweitert werden kann.
Im vierten Teil des Tutoriums wird beschrieben, wie man [[Klasse]]n in JavaScript nachbilden kann.
 
Vier Klassen werden implementiert: <code>Main</code>, <code>Ball</code>, <code>Paddle</code> und <code>Collision</code>.
Es wird jeweils ein Objekt pro Klasse erstellt.
 
[[Medium:MiniPong04Canvas.png|miniatur|ohne|709px|Das Datenmodell von MiniPong 04]]
 
=Anwendung „<code>MiniPongCanvas04</code>“=
 
==Neues Projekt anlegen==
 
Legen Sie ein neues Statisches Web-Projekt mit dem Namen <code>MiniPongCanvas04</code> an.
 
Speichern Sie dieses Projekt wie üblich in Ihrem Repository.
 
==Dateien erstellen==
 
Kopieren Sie die Dateien <code>index.html</code>, <code>css/main.css</code> und <code>js/CONSTANT.js</code> von [[HTML5-Tutorium: Canvas: MiniPong 03|Teil 3]] des Tutoriums,
passen Sie den Projekttitel in der Datei <code>index.html</code> an.
 
===<code>index.html</code>===
 
Jede Klasse soll in einer eigenen Datei definiert werden, wie dies auch bei anderen Programmiersprachen wie z.B. Java üblich ist.
Erstellen Sie zu diesem Zweck zunächst drei leere Dateien <code>js/ball.js</code>, <code>js/paddle.js</code> sowie <code>js/collision.js</code>
und fügen Sie anschließend folgende drei Zeilen in die Datei <code>index.html</code> ein (vor der Zeile, in der <code>js/main.js</code> geladen wird):
 
<source lang="javascript">
<script type="text/javascript" src="js/ball.js"    ></script>
<script type="text/javascript" src="js/paddle.js"  ></script>
<script type="text/javascript" src="js/collision.js"></script>
</source>
 
'''Anmerkung:''' Um den Kommunikations-Overhead zwischen Client (Browser) und Server möglichst gering zu halten,
sollte eine HTML-Seite möglichst wenige weitere Dokumente (Bilder, CSS-Dateien, JavaScript-Dateien etc.) nachladen.
Die Definition weiterer JavaSCript-Dateien stellt hier aber kein Problem dar, wenn – sobald die Anwendung fertiggestellt und ausgiebig getestet wurde –
alle JavaScript-Dateien komprimiert und in eine einzige Datei (<code>js/all.min.js</code>) eingefügt werden.
Natürlich darf in der Datei <code>index.html</code> anschließend auch nur noch diese eine JavaScript-Datei geladen werden.
 
===<code>ball.js</code>===
 
Die Definition der Klasse <code>Ball</code> wird in JavaScript durch zwei Objekte realisiert oder – besser gesagt – simuliert:
Eine Konstruktor-Funktion (genauer: ein Konstruktor-Funktion-Objekt) sowie das zugehörige <code>prototype</code>-Objekt.
 
Die Konstruktor-Funktion <code>Ball</code> erstellt neue Ball-Objekte und initialisiert diese,
indem sie im neu erstellten Objekt (<code>this</code>) Attribute erstellt und initialisiert.
 
====Konstruktor====
<source lang="javascript">
/**
*  Creates an instance of <code>Ball</code>.
*
*  @constructor
*  @this {Ball}
*  @param {CanvasRenderingContext2D} p_context
*        The 2d context of the canvas upon which the ball is to be drawed.
*  @param {number} p_r
*        The radius of the ball.
*  @param {number} [p_x_start = BALL_X]
*        The starting position of the ball in x-direction.
*  @param {number} [p_y_start = BALL_Y]   
*        The starting position of the ball in y-direction.
*  @param {number} [p_vx_start_min = BALL_VX_MIN]
*        The minimal starting velocity of the ball in x-direction.
*  @param {number} [p_vx_start_max = BALL_VX_MAX]
*        The maximal starting velocity of the ball in x-direction.
*  @param {number} [p_vy_start_min = BALL_VY_MIN]
*        The minimal starting velocity of the ball in y-direction.
*  @param {number} [p_vy_start_max = BALL_VY_MAX}
*        The maximal starting velocity of the ball in y-direction.
*/
function Ball(p_context,
              p_r, p_x_start, p_y_start,
              p_vx_start_min, p_vx_start_max,
              p_vy_start_min, p_vy_start_max
            )
{this.r = p_r || BALL_RADIUS;
 
  this.v_x_start        = p_x_start      || BALL_X;
  this.v_y_start        = p_y_start      || BALL_Y;
  this.v_vx_start_min  = p_vx_start_min || BALL_VX_MIN;
  this.v_vx_start_max  = p_vx_start_max || BALL_VX_MAX;
  this.v_vy_start_min  = p_vy_start_min || BALL_VY_MIN;
  this.v_vy_start_max  = p_vy_start_max || BALL_VY_MAX;
 
  this.v_vx_start_delta = this.v_vx_start_max - this.v_vx_start_min;
  this.v_vy_start_delta = this.v_vy_start_max - this.v_vy_start_min;
 
  this.v_context  = p_context;
 
  this.reset();
};
</source>
 
Die <code>reset</code>-Methode nimmt weitere Initialisierungen vor.
Da diese Initialisierungen bei jedem Spielstart erneut ausgeführt werden müssen,
wurden sie in eine Methode ausgelagert.
 
Beachten Sie bitte, dass der dem Konstruktor vorangestellte Kommentar [[JSDoc]]-konform ist.
Dies ermöglicht es, mit einem geeigneten JSDoc-Tool eine HTML-[[API]]-Dokumentation automatisch zu erstellen.
 
====Methoden====
 
Der Konstruktor weist jedem neu erstellten Objekt ein weiteres Attribut automatisch zu: <code>prototype</code>.
Dieses Attribut enthält einen Verweis auf das <code>prototype</code>-Objekt des zugehörigen Konstruktors.
JavaScript sucht bei einem Methodenaufruf <code>obj.m()</code> die Definition der Methode <code>m</code>
zunächst im Objekt <code>obj</code> selbst. Sollte dort keine Definition vorhanden sein, so
sucht JavaScript als nächstes in <code>obj.prototype</code>, dann in
<code>obj.prototype.prototype</code> usw. Die Suche endet sobald entweder eine Definition gefunden oder
kein weiteres <code>prototype</code>-Objekt mehr existiert. Im letzteren Fall meldet JavaScript einen Fehler.
 
Das heißt, in JavaScript werden Methoden üblicherweise im Prototyp-Objekt des Konstruktors definiert.
(Anmerkung: Auch hier werden wieder JSDoc-konforme Kommentare benutzt.)
 
<source lang="javascript">
Ball.prototype =
{ //////////////////////////////////////////////////////////////////////////////
  // data
  //////////////////////////////////////////////////////////////////////////////
 
  /** The radius of the ball. */ 
  get r()    { return this.v_r; },
  set r(p_r) { this.v_r = p_r;  },
 
  /** The x-position of the ball. */
  get x()    { return this.v_x; },
  set x(p_x) { this.v_x = p_x;  },
 
  /** The y-position of the ball. */
  get y()    { return this.v_y; },
  set y(p_y) { this.v_y = p_y;  },
 
  /** The vx-velocity of the ball. */
  get vx()    { return this.v_vx; },
  set vx(p_vx) { this.v_vx = p_vx; },
 
  /** The vy-velocity of the ball. */
  get vy()    { return this.v_vy; },
  set vy(p_vy) { this.v_vy = p_vy; },
 
  //////////////////////////////////////////////////////////////////////////////
  // logic
  //////////////////////////////////////////////////////////////////////////////
 
  /**
  * Resets the ball:
  *  Moves the ball to its starting position
  *  and computes randomly a starting velocity vector.
  */
  reset:
    function()
    { this.x  = this.v_x_start;
      this.y  = this.v_y_start;
      this.vx = (Math.random()<0.5?1:-1)
                * (this.v_vx_start_min+Math.random()*this.v_vx_start_delta);
      this.vy = (this.v_vy_start_min+Math.random()*this.v_vy_start_delta);
    },
 
  /** Moves the ball in direction (vx,vy); the step size depends on FPS. */
  move:
    function()
    { this.x += this.vx/FPS;
      this.y += this.vy/FPS;
    },
 
  //////////////////////////////////////////////////////////////////////////////
  // view
  //////////////////////////////////////////////////////////////////////////////
 
  /** Draws the ball at its current position onto a 2d context. */
  draw:
    function()
    { this.v_context.beginPath();
      this.v_context.arc(this.x, this.y, this.r, 0, 2*Math.PI, true);
      this.v_context.lineWidth = BALL_BORDER_WIDTH;
      this.v_context.lineStyle = BALL_BORDER_COLOR;
      this.v_context.fillStyle = BALL_COLOR;
      this.v_context.stroke();
      this.v_context.fill();
    },
 
  //////////////////////////////////////////////////////////////////////////////
  // end of prototype
  //////////////////////////////////////////////////////////////////////////////
};
</source>
 
Beachten Sie, dass die [[Getter-Methode|Getter]]- und [[Setter-Methode]]n für die
Attribute <code>r</code>, <code>x</code>, <code>y</code>, <code>vx</code>, <code>vy</code>
EcmaScript-5-konform (HTML5) definiert wurden. Das hat den Vorteil, dass man mit
der üblichen Attribut-Syntax auf diese Attribute zugreifen kann:
 
<source lang="JavaScript">
var my_ball = new Ball();
 
my_ball.r = 20;        // Aufruf der Setter-Methode
console.log(my_ball.r); // Aufruf der Getter-Methode
</source>
 
In EcmaScript 3 (HTML 4 oder früher)
gibt es diese Art der Getter- und Setter-Methoden nicht. Hier muss man auf
den in Java üblichen Trick zurückgreifen und ersatzweise zwei Methoden definieren.
 
<source lang="JavaScript">
/** The radius of the ball. */ 
getR: function()    { return this.v_r; },
setR: function(p_r) { this.v_r = p_r;  },
 
...
</source>
 
In diesem Fall erfolgen die Attribut-Zugriffe mittels Methodenaufrufen:
 
<source lang="JavaScript">
var my_ball = new Ball();
 
my_ball.setR(20);            // Aufruf der Setter-Methode
console.log(my_ball.getR()); // Aufruf der Getter-Methode
</source>
 
Die Verwendung von echten Getter- und Setter-Methoden erhöht die
[[Programmierprinzipien#Stetigkeit.2C_Continuity|Stetigkeit]] des Codes,
da sich der Zugriff auf öffentliche Zustandsvariablen syntaktisch nicht vom
Zugriff auf echte Getter- und Setter-Methoden unterscheidet. Das heißt,
die Implementierung der Klasse kann jederzeit die Art der Attribut-Definition ändern,
ohne dass das Auswirkungen auf die Zugriff-Syntax von zugehörigen Objekten hätte.<ref>vgl. {{Quelle|Meyer (1997)}}</ref> 
 
Beispielsweise könnten in der obigen Definition der Klasse <code>Ball</code>
die Getter- und Setter-Methoden der Attribute <code>x</code> und <code>y</code>
(und alle anderen Getter- und Setter-Methoden) einfach aus dem Prototype-Objekt gelöscht werden.
Am Verhalten der Klasse <code>Ball</code> würde das nichts ändern, da die Methoden
<code>reset</code> und <code>move</code> nun direkt auf die Zustandsvariablen
<code>this.x</code> und <code>this.y</code> zugreifen würden. Der zugehörige
Code wäre sogar etwas effizienter.
 
Kommentieren Sie in der Musterlösung oder in Ihrer finalen Lösung
doch einfach mal ein paar Getter- und Setter-Methoden aus und starten Sie die Anwendung erneut. Anschließend
sollten Sie die Methoden wieder aktivieren und testweise einmal einen Log-Befehl einfügen:
 
<source lang="javascript">
set x(p_x) { this.v_x = p_x; console.log("Setter 'x', param: " + p_x); },
</source>
 
Wenn Sie jetzt das die Anwendung ausführen, wird jede Änderung der x-Position des Balls
im Konsolfenster ausgegeben. Ohne echte Setter-Methode hätten Sie diesen Logging-Befehl
an jede Stelle, an der die Ballposition geändert wird, einfügen mussen. Oder Sie hätten
an jeder dieser Stellen <code>...ball.x = ...</code> durch <code>...ball.setX(...)</code>
ersetzen müssen.
 
===<code>paddle.js</code>===
 
Die Klasse <code>Paddle</code> wird analog zur Klasse <code>Ball</code> definiert.
Die benötigten Attribute und MEthoden findet man im obigen Klassendiagramm.
 
Die Methoden-Definitionen von <code>reset</code>, <code>move</code> und </code>draw</code> werden
(wie bei der Klasse <code>Ball</code> aus) der Lösung des [[HTML5-Tutorium: Canvas: MiniPong 03|dritten Teils des Tutoriums]] übernommen.
Bei der Definition der Methode <code>reset</code> sollte analog zur Klasse <code>Ball</code>
auf der Zugriff auf globale Konstanten (</code>PADDLE_X</code> und </code>PADDLE_Y</code>)
durch den Zugriff auf lokale Zustandsvariablen (</code>this.v_x_start</code> und </code>this.v_y_start</code>)
ersetzt werden. Dies Variablen sollten (wie in der Klasse <code>Ball</code>) von Konstruktor initialisiert werden.
 
Zusätzlich werden drei weitere Methoden benötigt:
 
<source lang="javascript">
/** Makes the paddle move left (if it is currently not moving). */ 
startLeft:  function(){ if (this.vx == 0) this.vx = -this.v_vx_start; },
 
/** Makes the paddle move right (if it is currently not moving). */ 
startRight: function(){ if (this.vx == 0) this.vx = +this.v_vx_start; },
 
/** Makes the paddle stop moving. */ 
stop:      function(){ this.vx = 0; },
</source>
 
Die Attribute werden genauso definiert wie in der Klasse <code>Ball</code>:
Entweder gar nicht oder mittels echten Setter- und Getter-Methoden.
Vier Atribute wurden allerdings im Diagramm als nur lesbar (read only, <code>frozen</code>)
deklariert: <code>left</code>, <code>right</code>, <code>top</code> und <code>bottom</code>.
Diese Attribute dürfen also nicht geändert sondern nur gelesen werden,
da die Werte aus anderen Attributen berechnet werden. Dies kann mit Hilfe echter Getter-Methoden
recht elegant realisiert werden:
 
<source lang="javascript">
/** The left side of the paddle (read only). */
get left() { return this.x; },
 
/** The right side of the paddle (read only). */
get right() { return this.x + this.width; },
 
/** The top side of the paddle (read only). */
get top() { return this.y;  },
 
/** The bottom side of the paddle (read only). */
get bottom() { return this.y + this.height; },
</source>
 
Auf diese Attribute kann ebenfalls mit der üblichen Attribut-Syntax zugegriffen werden:
 
<source lang="javascript">
var my_paddle = new Paddle();
 
console.log(my_paddle.left); // Aufruf der Getter-Methode
</source>
 
===<code>main.js</code>===
 
{{TBD}}
 
=Weitere Verbesserungsmöglichkeiten=
 
{{Codequalität
| application    = MiniPongCanvas04
| readability    = 4
| writability    = 6
| continuity      = 4
| customizability = 4
| dry            = 3
| demeter        = 5
| verifiability  = 2
| interfaces      = 6
| contract        = 6
| liskov          = 6
| modularity      = 3
}}
 
==Das Prinzip der „[[Programmierprinzipien#Verst.C3.A4ndlichkeit.2C_Comprehesibility.2C_Lesbarkeit.2C_Readability|Verständlichkeit/Lesbarkeit]]“==
 
{{TBD}}
 
==Das Prinzip der „[[Programmierprinzipien#Schreibbarkeit.2C_Writability|Schreibbarkeit]]“==
 
{{TBD}}
 
==Das Prinzip der „[[Programmierprinzipien#Stetigkeit.2C_Continuity|Stetigkeit]]“==
 
{{TBD}}
 
==Das Prinzip der „[[Programmierprinzipien#Konfigurierbarkeit.2C_Customizability|Konfigurierbarkeit]]“==
 
{{TBD}}
 
==Das Prinzip „[[Don't repeat yourself]]“==
 
{{TBD}}
 
==Das „[[Programmierprinzipien#Gesetz_von_Demeter.5B5.5D.2C_Law_of_Demeter.2C_LoD|Gesetz von Demeter]]“==
 
{{TBD}}
 
==Das Prinzip der „[[Programmierprinzipien#.C3.9Cberpr.C3.BCfbarkeit.2C_Verifiability|Überprüfbarkeit]]“==
 
{{TBD}}
 
==Das Prinzip „[[Programmierprinzipien#Benutze_Integrit.C3.A4tsbedingungen.2C_Make_Use_of_Integrity_Constraints.2C_Design_by_Contract.5B2.5D|Design by Contract]]“==
 
{{TBD}}
 
==Das  „[[Programmierprinzipien#Liskovsches_Substitutionsprinzip.5B3.5D.2C_LSP.2C_Ersetzbarkeitsprinzip.2C_Liskov_substitution_principle.5B4.5D|Liskovsche Substitutionsprinzip]]“==
 
{{TBD}}
 
==Das Prinzip der „[[Programmierprinzipien#Modularit.C3.A4t.2C_Modularity.2C_Teile_und_herrsche.2C_Divide_et_impera|Modularität]]“==


{{TBD}}
{{TBD}}


=Quellen=
===Klassenmodell===
<references/>
[[Datei:MiniPong05 ClassModel Overview.png|gerahmt|rechts|Klassendiagramm]]
<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 das [[HTML-Tutorium: SVG: Hello World|Hello-World-SVG-Tutorium]] bearbeiten, sofern Sie dies noch nicht gemacht haben.
==Quellen==
Anderenfalls können Sie gleich mit dem [[HTML-Tutorium: SVG: MiniPong|Minipong-SVG-Tutorium]] forfahren.
# {{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]]

Aktuelle Version vom 9. Dezember 2016, 18:53 Uhr

Dieser Artikel erfüllt die GlossarWiki-Qualitätsanforderungen nur teilweise:

Korrektheit: 3
(zu größeren Teilen überprüft)
Umfang: 3
(einige wichtige Fakten fehlen)
Quellenangaben: 5
(vollständig vorhanden)
Quellenarten: 5
(ausgezeichnet)
Konformität: 5
(ausgezeichnet)

HTML-Tutorium: MiniPong

MiniPong: | Teil 1 | Teil 2 | Teil 3 | Teil 4 | Teil 5

Musterlösung: index.html (WK_MiniPong05 (SVN))

Ziel: Das fertige Spiel „MiniPong“ verbessern

Im fünften Teil des Tutoriums wird beschrieben, wie die funktionsfähige Version von MiniPong verbessert und erweitert werden kann.

TO BE DONE

Klassenmodell

Klassendiagramm


Quellen

  1. Kowarschick (MMProg): Wolfgang Kowarschick; Vorlesung „Multimedia-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2018; Quellengüte: 3 (Vorlesung)