<html>
<
head>
<
title>HTML5 Pong 2player by samweis78</title>
<!--
      
HTML5 Pong 2player
      geschrieben von samweis78 für codingwall
.blogspot.com
      Kontaktmöglichkeit über das Blog
.
      
      
Dieses Spiel ist das Ergebnis einer kleinen Coding-Battle, die sich durii
      und ich am 16. Juli 2011 lieferten
Ich sag malich hab klar gewonnenda
      duriis Pong bis heute nicht fertig ist
.
      
Ich geb aber auch zuich hatte ein wenig KenntnisvorsprungJavaScript
      läuft mir seit 13 Jahren regelmäßig über den Weg und einige Tage davor
      hatte ich ernsthaft begonnen
mich in das Canvas-Thema einzuarbeiten.
      
Dies ist aber mein erstes Projekt in die Richtungdas ich als "fertig"
      
bezeichnen würde.
      
      
Externe Quellen habe ich angegebenDas ist aber nur die Funktion für die
      
Abfrage der gedrückten Tasten.

      
Tutorials musste ich auch noch befragendas waren diese:
       
http://www.html5canvastutorials.com
       
http://canvas.quaese.de/
       
      
Ich arbeite momentan auch noch an einer Version mit Computergegner und bin
      ganz scharf drauf
eine Online-Multiplayer zu programmieren mit jQuery und
      JSON
-PaketenDas muss aber grad noch etwas vor anderen Projekten
      zurückstehen
.
       
      
Achsovielleicht sollte ich noch dazuschreibendass ich das Original-
      
Spiel nie ernsthaft gespielt habeweshalb ich jetzt nicht weissob sich
      
die "Physik" richtig anfühltoder die Spielrunde bis zum 10. Punkt denn
      originalgetreu ist
.
      
Das ist einfach meine Version

      
Ausserdem rammelt sich der Ball manchmal hinter den Balken festDas habe
      ich beim Probespielen schon gemerkt
hatte aber nicht mehr die Kraftdas
      zu verbessern
Man möge es mir vergeben.
       
 -->
<
script type="text/javascript">

/*
    Zuerst werden reichlich Variablen und Objekte initialisiert.
*/

var maxX 640;                                      //Die Breite des Canvas
var maxY 320;                                      //Die Höhe des Canvas... ja, hätt ich auch dynamisch machen können. Hab ich aber nicht.
var punkteLinks 0;                                 //Variable für die Punkte, die erspielt werden.
var punkteRechts 0;                                //s.o.
var stdBalkenHeight 100;                           //Standardwert für die Höhe der Balken. Objekte werden damit initialisiert 
var stdBalkenY = (maxY-stdBalkenHeight)/2;           //Standardwert für die Y-Koordinate eines Balkens, sodass er mittig gesetzt wird.
var menu true;                                     //Indikator, dass sich das Spiel im Menü befindet.

var linkerBalken = new Object();                     //Der linke Balken wird als Objekt angelegt und initialisiert.
linkerBalken["height"] = stdBalkenHeight;            //Höhe (habe ich über die std-Variable gemacht wegen des Hintergedankens der Veränderung während des Spiels... soweit hab ichs aber nicht getrieben)
linkerBalken["width"] = 15;                          //Breite
linkerBalken["color"] = "#ccc";                      //Farbe
linkerBalken["x"] = 20;                              //Position vom linken Rand entfernt
linkerBalken["y"] = stdBalkenY;                      //vertikale Position = Mitte
linkerBalken["speed"] = 20;                          //Geschwindigkeitsvariable. Pro Tastendruck kann man den Balken um 20px verschieben.

var rechterBalken = new Object();                    //Das Objekt für den rechten Balken. Soweit alles identisch...
rechterBalken["height"] = stdBalkenHeight;
rechterBalken["width"] = 15;
rechterBalken["color"] = "#ccc";
rechterBalken["x"] = 640-rechterBalken["width"]-20;  //...bis auf die Berechnung der x-Koordinate
rechterBalken["y"] = stdBalkenY;
rechterBalken["speed"] = 20;

var 
spielBall = new Object();                        //Das Objekt des Balls
spielBall["x"] = 640/2;                              //Position wird mittig initialisiert.
spielBall["y"] = 320/2;                              //diese auch
spielBall["dirX"] = 10;                              //die Eigenschaft der Geschwindigkeit auf der horizontalen Ebene
spielBall["dirY"] = 0;                               //Vertikale Geschwindigkeit. Ist Anfangs immer 0, er fliegt also gerade, waagerecht
spielBall["radius"] = 10;                            //Der Radius, mit dem der Ball später gezeichnet wird.
spielBall["color"] = "#DDD";                         //Die Farbe.

var readySetGo = new Object();                       //Das ist ein Objekt für diesen kleinen Countdown, der vor dem Balleinwurf gezeigt wird.
readySetGo["max"] = 25;                              //Anzahl der Frames, die er immer laufen soll
readySetGo["countdown"] = 0;                         //Eine Eigenschaft, die später von ["max"] bis 0 runtergezählt wird.


function doKeyDown(evt){                                                          //Diese Funktion wird beim Event eines Tastendrucks aufgerufen
//http://html5.litten.com/moving-shapes-on-the-html5-canvas-with-the-keyboard/    
//Keycodes: http://www.mediaevent.de/javascript/Extras-Javascript-Keycodes.html
  
switch (evt.keyCode)
  {
    case 
38:  /* Up arrow */                                                      //Hoch-Taste
      
if(rechterBalken["y"]-rechterBalken["speed"]>0)                             //Wenn der rechte Balken mit diesem Schritt nicht aus dem Spielfeld gehen würde,
        
rechterBalken["y"]-=rechterBalken["speed"]                                //wird seine Y-Position um seine Eigenschaft ["speed"] verringert
    
break;
    case 
40:  /* Down arrow */                                                    //Runter-Taste
      
if(rechterBalken["y"]+rechterBalken["height"]+rechterBalken["speed"]<maxY)  //Wenn der rechte Balken mit diesem Schritt nicht aus dem Spielfeld gehen würde,
        
rechterBalken["y"]+=rechterBalken["speed"]                                //wird seine Y-Position um seine Eigenschaft ["speed"] erhöht
    
break;
    case 
83:  /* s  pressed */                                                    //s-Taste (Hoch für den linken Spieler)                                                                   
      
if(linkerBalken["y"]-linkerBalken["speed"]>0)                               //Wenn der linke Balken mit diesem Schritt nicht aus dem Spielfeld gehen würde,
        
linkerBalken["y"]-=linkerBalken["speed"]                                  //wird seine Y-Position um seine Eigenschaft ["speed"] verringert               
    
break;                                                                                                                                                        
    case 
88:  /* x pressed */                                                     //x-Taste (Runter für den linken Spieler)                                                                  
      
if(linkerBalken["y"]+linkerBalken["height"]+linkerBalken["speed"]<maxY)     //Wenn der linke Balken mit diesem Schritt nicht aus dem Spielfeld gehen würde,
        
linkerBalken["y"]+=linkerBalken["speed"]                                  //wird seine Y-Position um seine Eigenschaft ["speed"] erhöht                   
    
break;
    case 
32:  /* Space pressed */                                                 //Leertaste
      
if(menu==true)                                                              //Wenn das Menü aktiv ist
      
{
        
menu false;                                                             //Wird das Menü deaktiviert
        
readySetGo["countdown"] = readySetGo["max"];                              //Der Countdown-Counter wird auf seinen Maximalwert gesetzt
        
punkteLinks 0;                                                          //Punkte für beide Spieler...
        
punkteRechts 0;                                                         //...werden auf 0 gesetzt.
      
}
    break;
  }
}

function 
changeBallAngle(Balken)                                                  //Diese Funktion übernimmt die Berechnung des Richtungswechsels des Balls,
{                                                                                 //Je nachdem, wie weit entfernt von der Mitte des Balkes er auftrifft.
  
var h;
  
= ( Balken["y"] - spielBall["y"] + Balken["height"] / ) * -1;               //h ist die Entfernung des Auftrittspunktes des Balls auf den Balken von der Mitte aus. Negativ wenn oberhalb, positiv wenn unterhalb.
  
spielBall["dirY"]+=h/5;                                                         //Die Eigenschaft für die vertikale Geschwindigkeit wird auf 1/5 von h gesetzt.
}

function 
neuerBall()                                                              //Diese Funktion übernimmt einige Reset-Aufgaben bevor es einen neuen Spielball gibt 
{
  
spielBall["x"] = Math.round(maxX/2);                                            //Ball wird...
  
spielBall["y"] = Math.round(maxY/2);                                            //...zentriert
  
spielBall["dirY"] = 0;                                                          //Vertikale Geschwindigkeit des Balls auf 0 gesetzt
  
spielBall["dirX"] = 10;                                                         //horizontale Geschwindigkeit auf 10 (wobei die auch nirgends geändert wird... naja)
  
linkerBalken["y"] = stdBalkenY;                                                 //Balken werden...
  
rechterBalken["y"] = stdBalkenY;                                                //...vertikal zentriert
  
if(Math.random()<0.5spielBall["dirX"] *= -1;                                  //eine 50/50-Möglichkeit wird ausgewürfelt, ob der Ball zuerst nach links oder rechts fliegt
  
readySetGo["countdown"] = readySetGo["max"];                                    //Der GetReady-Counter wird auf seinen Maximalwert gesetzt, wodurch er gleich gezeigt wird.

}
/*
   So, das war das notwendige Vorgeplänkel und - ich nenns mal unfachmännisch - der "passive Code".
   Ich hab schon nen Tennisarm vom Kommentieren, aber nu muss ich da durch!
*/


            
window.onload = function()                                          // Ausführen des folgenden Codes erst, wenn die Seite vollständig geladen ist
            
{                                           
                var 
canvas document.getElementById("pongCanvas");               // ich schnapp mir den Canvas, auf dem ich zeichnen möchte
                
var context canvas.getContext("2d");                            // und batsch mir einen Context drauf.

                
window.addEventListener('keydown',doKeyDown,true);                // Ein EventListener wird eingehakt, der bei KeyDown meine Funktion doKeyDown aufruft
 
                
setInterval(function()                                          //So, und hier wird's endlich interessant. Die Funktion setInterval führt "was auch immer" 
                
{                                                                 // immer wieder aus. In diesem Fall sogar 25 mal die Sekunde: setInterval(function(){}, (1000/25));
                                                                                  // und weil sie das tut, kann ich darin eine flüssige Animation darstellen.
                                                                                  // Der folgende Code wird also 25x je Sekunde ausgeführt. U.A. Cleare ich also den Canvas 
                                                                                  // 25 mal je Sekunde und zeichne ihn neu.
                
                  
if(punkteLinks || punkteRechts 9menu true;          //Wenn die Maximalpunktzahl von 10 erreicht wurde, wird das Menü aktiviert
                
                  
if(menu==true)                                                //Wenn das Menü aktiv ist, wird es gezeichnet:
                  
context.beginPath();                                          //Den ganzen Zeichen-Kram erkläre ich nicht einzeln. Dafür sind die o.g. Tutorials da.
                    
context.strokeStyle "black";             
                    
context.fillStyle   "#ccc";
                    
context.font        "20pt Calibri";
                    if( 
punkteLinks punkteRechts )                              //Falls die Punkte schon != 0 sind, wird das Match-Ergebnis dargestellt.
                      
context.fillText("Left player won!"23040);
                    else if( 
punkteLinks punkteRechts )
                      
context.fillText("Right player won!"23040);
                    
                      
                    
context.fillText("HTML5"230120);                          //Das Menü
                    
context.font "40pt Calibri";
                    
context.fillText("Pong"310120);
                    
context.font "15pt Calibri";
                    
context.fillText("by samweis78"270137);
                    
context.font "20pt Calibri";
                    
context.fillText("Left player uses keys S and X"160200);
                    
context.fillText("right player uses the keys Up and Down"120220);
                    
context.fillText("Press Space to start Game"185300);
                    
context.stroke();
                  }
                  else if(
readySetGo["countdown"] > 0)                            //Wenn das Menü nicht gesetzt ist, aber der readySetGo Countdown noch nicht runtergezählt wurde, wird das hier erledigt:
                  
{
                    
context.clearRect maxX maxY );                    //Canvas leeren
                    
context.beginPath();
                    
context.rect(20020024050);
                    
context.fillStyle "black";
                    
context.fill();
                    
context.lineWidth 10;
                    
context.strokeStyle "#ccc";
                    
context.stroke();                                             //Reechteck gezeichnet
                    

                    
context.font "15pt Calibri";
                    
context.fillStyle "#ccc";
                    
context.fillText("Ready, set, GO!"260220);
                    
context.stroke();                                             //Text geschrieben

                    
context.beginPath();                                          //Fortschrittsbalken
                    
context.rect(200230Math.round(240/readySetGo["max"]*readySetGo["countdown"]), 20); //Das Rechteck wird kleiner, je kleiner ["countdown"] ist
                    
context.fillStyle "#ccc";
                    
context.fill();
                    
context.lineWidth 10;
                    
context.strokeStyle "#ccc";
                    
context.stroke();                                             //Fortschrittsbalken gezeichnet

                    
readySetGo["countdown"]--;                                    //Das Runterzählen des Countdown-Counters nicht vergessen!
                  
}
                  else                                                          
//Wenn weder Menü, noch readySetGo-Counter aktiv sind, dann wird gespielt!
                  
{
                    
// Bewegung des Balls inkl. abprallen am Rand
                    
if((spielBall["x"]-spielBall["radius"])<0-spielBall["radius"])      { punkteRechts++; neuerBall(); } //Ball fliegt aus Spielfeld. PUNKT! für links
                    
if((spielBall["x"]+spielBall["radius"])>maxX+spielBall["radius"])   { punkteLinks++;  neuerBall(); } //Ball fliegt aus Spielfeld. PUNKT! für rechts
                    
if((spielBall["y"]-spielBall["radius"])<0)    { spielBall["dirY"] *= -1; } //Abprallen oben
                    
if((spielBall["y"]+spielBall["radius"])>maxY) { spielBall["dirY"] *= -1; } //Abprallen unten
                    
spielBall["x"]+=spielBall["dirX"];                                         //Der Pall ändert seine X-Position um seine X-Geschwindigkeit
                    
spielBall["y"]+=spielBall["dirY"];                                         //dito mit Y
                    
                    //Kollisionsabfrage Balken mit Ball                                        //Das ist mal ne umfangreiche Bedingung... muss ich die jetzt erklären?
                     //Linker Balken
                     
if( ( (spielBall["x"]-spielBall["radius"]) < (linkerBalken["x"]+linkerBalken["width"]) )  &&   //Kurz gesagt: Wenn die linke Außenseite des Balls (x-radius) kleiner
                         
(spielBall["y"] > linkerBalken["y"] ) &&                                                   //als die rechte Seite des Balkens (y+width) ist und die Y-Koordinate  
                         
(spielBall["y"] < ( linkerBalken["y"] + linkerBalken["height"] ) )                         //des Balls auch noch größer als die des linken Balkens und kleiner     
                       
)                                                                                            //als die Unterkante desselben ist (y+height), dann berühren sie sich!
                     
//Ball kollidiert mit Vorderseite des linken Balkens
                       
spielBall["dirX"]*=-1;                                                                       //Die Richtung des Balls wird geändert
                       
changeBallAngle(linkerBalken);                                                               //und die Funktion changeBallAngle (s.o.) ändert seinen Flugwinkel.
                     
}  
  
                     
//rechter Balken
                     
if( ( (spielBall["x"]+spielBall["radius"]) > rechterBalken["x"] )  &&                          //analog zum Linken. Ich kann nich mehr...
                         
(spielBall["y"] > rechterBalken["y"] ) &&
                         (
spielBall["y"] < ( rechterBalken["y"] + rechterBalken["height"] ) )
                       )
                     { 
//Ball kollidiert mit Vorderseite des linken Balkens
                       
spielBall["dirX"]*=-1;
                       
changeBallAngle(rechterBalken);      
                     }                   
  
                    
/*
                       Das Berechnen der Spielmechanik ist beendet. Jetzt wird nur noch gezeichnet.
                    */
                    
                    
context.clearRect maxX maxY );                                                               // Canvas leeren

                    
context.beginPath();
                    
context.lineWidth 1;
                    
context.rect(linkerBalken["x"], linkerBalken["y"], linkerBalken["width"], linkerBalken["height"]);       //Linker Balken
                    
context.rect(rechterBalken["x"], rechterBalken["y"], rechterBalken["width"], rechterBalken["height"]);   //Rechter Balken
                    
context.fillStyle linkerBalken["color"];
                    
context.fill();
                    
context.stroke();
                    
                    
context.beginPath();
                    
context.arc(spielBall["x"], spielBall["y"], spielBall["radius"], 0Math.PIfalse);                 //Der Ball
                    
context.fillStyle spielBall["color"];
                    
context.fill();
                    
context.stroke();
  
                    
context.font "20pt Calibri";                                                                           //Die Punkte oben links und rechts.
                    
context.fillText(punkteLinks520);
                    
context.fillText(punkteRechtsmaxX-3020);
                  }
                }, (
1000/25)); // von setInterval(function()  1000/25 = 25 Frames pro Sekunde
                              
            
}; // von window.onload



</script>
<style>
  body{
         text-align:center;
         background-color:black;
  }
  #pongCanvas { /*Der Rahmen um das Spielfeld fiel mir erst später ein, deshalb ist er einfach mit CSS gemacht.*/
         border-top: 10px solid #ccc;
         border-bottom: 10px solid #ccc;
         border-left: 10px dotted #ccc;
         border-right: 10px dotted #ccc;
  }
</style>
</head>
<body>
  <canvas width="640" height="320" id="pongCanvas" style="background-color:black;"></canvas> 
  <!--
    Und mehr muss hier nicht stehen.
  -->
</body>