HTML5-Tutorium: Canvas: MiniPong 03

aus GlossarWiki, der Glossar-Datenbank der Fachhochschule Augsburg

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_MiniPong02 (SVN))

Ziel: Interaktion mit dem Schläger

Im dritten Teil des Tutoriums wird beschrieben, wie man die Interaktion mit dem Schläger realisiert.

Neues Projekt anlegen

Legen Sie ein neues Projekt mit dem Namen „MiniPong03“ an.

Kopieren Sie die Dateien der App3 des Projektes MiniPong03 in das neue Projekt.

Nehmen Sie folgende Anpassungen vor:

  • Umbenennung des Ordners „app3“ in „app“.
  • Umbenennung der Datei „main3.js“ in „main.js“.
  • Umbenennung der Datei „index3.html“ in „index.html“.
  • In der Datei „main.js“:
    • Ersetzen von „app: 'app3'“ durch „app: 'app'“.
  • In der Datei „index.html“:
    • Ändern des Titels in „MiniPong03“:
    • Ersetzen von „js/main3“ durch „js/main“ im Script-Befehl.

Nun sollte die Anwendung wieder laufen.

CONSTANT.js

"use strict";
 
const 
CANVAS_WIDTH  = 400,
CANVAS_HEIGHT = 300,

FPS = 50, // Frames Per Second

KEY_LEFT  = 37,
KEY_RIGHT = 39,

PADDLE_WIDTH    =  50,
PADDLE_HEIGHT   =   8,
PADDLE_X        = (CANVAS_WIDTH - PADDLE_WIDTH) / 2,
PADDLE_Y        = CANVAS_HEIGHT - PADDLE_HEIGHT - 5,
PADDLE_VX_START = 100, // pixels per second
PADDLE_AX       = 500, // pixels per second

PADDLE_COLOR    = "#666666";

main.js

Die JavaScript-Datei main.js ist prinzipiell genauso aufgebaut, wie main.js in Teil 1 des Tutoriums. Sie enthält ein Schläger-Objekt (Paddle), speichert den 2D-Kontext und den Animationstimer in zwei weiteren Variablen und initielsiert die Anwendung, nachdem die HTML-Seite vollständig geladen wurde, mit Hilfe einer >code>init-Funktion. Auch die Observer-Funktion o_redraw gibt es wieder.

Die Attribute des Schlägers unterscheiden sich etwas von den Attributen des Balls aus dem ersten Teil des Tutoriums. Der Schläger kann sich nur in x-Richtung (hin- und her-) bewegen, daher wird der Wert vy (= Geschwindigkeit in y-Richtung) nicht gespeichert. Der Schläger bewegt sich zunächst nicht. Er fängt erst mit der Bewegung an, sobald der Spieler eine bestimmte Taste betätigt. Die Start-Geschwindigkeit wird im Attribut vx_start gespeichert. Je länger der Spieler die Taste betätigt, desto schneller bewegt sich der Schläger. Der zugehörige Beschleunigungsfaktor wird im Attribut ax gespeichert.

"use strict";

var g_context, // the 2d context of the canvas
    g_timer,   // the "physics engine" timer
    g_paddle = // the paddle
    { width:  PADDLE_WIDTH,
      height: PADDLE_HEIGHT,
      x:      PADDLE_X,
      y:      PADDLE_Y,
      vx:     0,           // initially the paddle is not moving 
      ax:     PADDLE_AX, 

      // Moves the paddle in direction (vx, 0) and accelerates it;
      // the step size depends on FPS.
      move:
        function()
        { this.x  += this.vx/FPS;
          this.vx += this.ax/FPS * (this.vx > 0 ? 1 : -1);
        },

      // Draws the paddle at its current position onto a 2d context.
      draw:
        function(p_context)
        { p_context.beginPath();
          p_context.fillStyle = PADDLE_COLOR;
          p_context.rect (this.x, this.y, this.width, this.height);
          p_context.fill();
        }
    };

// An event observer: 
// It is called every 1000/FPS milliseconds.
function o_redraw() 
{ // Move the paddle, if possible (collision detection and response)
  if (   (g_paddle.vx < 0 && g_paddle.x > 0)
      || (g_paddle.vx > 0 && g_paddle.x + g_paddle.width < CANVAS_WIDTH)
     )
  { g_paddle.move(); }

  // Clear and redraw the canvas.
  g_context.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
  g_paddle.draw(g_context);
}

// An event observer: 
// It is called whenever a "start paddle moving event" is signaled.
function o_start_paddle_moving(p_event)
{ if (g_paddle.vx == 0)  // react only if the paddle is not already moving
  { if (p_event.keyCode == KEY_LEFT)
    { g_paddle.vx = -PADDLE_VX_START; }
    else if (p_event.keyCode == KEY_RIGHT)
    { g_paddle.vx = +PADDLE_VX_START; }
  }
}

// An event observer: 
// It is called whenever a "stop paddle moving event" is signaled.
function o_stop_paddle_moving(p_event)
{ g_paddle.vx = 0; }

In der Datei main.js müssen noch zwei weitere Observer-Funktionen definiert werden: o_start_paddle_moving und o_stop_paddle_moving.

In der zuvor definierten init-Funktion wurde festgelegt, dass eine der Funktionen jedesmal dann ausgeführt wird, wenn ein spezielles Ereignis eintritt:

  • Spieler drückt eine Taste: o_start_paddle_moving wird aufgerufen.
  • Spieler lässt eine Taste los: o_stop_paddle_moving wird aufgerufen.

Im ersten Fall wird – sofern sich der Schläger noch nicht bewegt – nachgesehen, welche Taste der Spieler gedrückt hat. Falls es eine der beiden Steuerungstasten des Schläger ist, wird der Schläger in die richtige Richtung in Bewegung gesetzt.

Im zweiten Fall wird der Schläger einfach gestoppt, egal ob und in welche Richtung er sich gerade bewegt. Diese Art der Steuerung ist zugegebenermaßen etwas grob. Man kann beispielsweise den Schläger zunächst mit einem Tastendruck starten kann und dann eine belebige zweite Taste drücken (ohne die erste Taste loszulassen). Wenn man die zweite Taste loslässt, stoppt die Schlägerbewegung, obwohl diese eigentlich nichts mit der Schlägersteuerung zu tun hat. Wenn Sie diese Verhaltensweise stört, können Sie den Code der Stopp-Funktion gerne etwas weniger „grob“ formulieren.

// An event observer: 
// It is called whenever a "start paddle moving event" is signaled.
function o_start_paddle_moving(p_event)
{ if (g_paddle.vx == 0)  // react only if the paddle is not already moving
  { if (p_event.keyCode == KEY_LEFT)
    { g_paddle.vx = -PADDLE_VX_START; }
    else if (p_event.keyCode == KEY_RIGHT)
    { g_paddle.vx = +PADDLE_VX_START; }
  }
}

// An event observer: 
// It is called whenever a "stop paddle moving event" is signaled.
function o_stop_paddle_moving(p_event)
{ g_paddle.vx = 0; }

Zu guter letzt muss die Anwendung wie üblich initialisiert werden:

// 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");
  
  // 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);
}

// Execute the init function after the HTML page has been loaded.
window.onload = f_init;

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.

Das Objektmodell von MiniPong 02a

Beachten Sie wieder das Programmierprinzip „Don't repeat yourself“ (Dry).

Musterlösung: Minipong 2a (SVN-Repository)

Quellen

  1. Braun (2011): Herbert Braun; Webanimationen mit Canvas; in: c't Webdesign; Band: 2011; Seite(n): 44–48; Verlag: Heise Zeitschriften Verlag; Adresse: Hannover; 2011; Quellengüte: 5 (Artikel)
  2. Kowarschick (MMProg): Wolfgang Kowarschick; Vorlesung „Multimedia-Programmierung“; Hochschule: Hochschule Augsburg; Adresse: Augsburg; Web-Link; 2018; Quellengüte: 3 (Vorlesung)

Fortsetzung des Tutoriums

Sie sollten nun Teil 3 des Tutoriums bearbeiten.