Main sections
13 Das PONG Spiel
PONG
Was ist PONG
Jetzt, da die Grundlagen klar sind, muss das Gelernte gefestigt und geübt werden. Das Spiel heißt: "PONG". Es ist nicht nur eines Deiner ersten Spiele mit GLBasic, sondern auch das erste Computerspiel der Welt. (1972 von Ralph H. Baer)
Wenn Du PONG nicht kennst, sieh hier nach:
www.pong-story.com
Neues Projet
Nach dem Start von GLBasic den Kopf "Neues Projekt" auswählen, und einen Namen + Pfad angeben. z.B. ..\GLBasic\Projects\Pong
Variablen
Nachdem das Projekt angelegt ist, können wir schon mit der Programmierarbeit anfangen.
Wir brauchen Variablen für die Schlägerpositionen x und y, sowie die bisher erzielten Punkte. Für all diese Variaben legen wir je ein Feld an, damit später 3,4 oder mehr Spieler einfach eingebaut werden könnten. Auch kann man so schnell den Code für Schlägerbewegung und Punktestand in einer Schleife wiederverwenden. Später mehr dazu...
Unser Code sieht jetzt so aus:
// Pong
DIM bat_y[2]
DIM bat_x[2]
DIM score[2]
Nun müssen wir die Spieler initialisieren. Dazu fügen wir eine SUB hinzu mit dem Menüpunkt: "Projekt/Neue SUB". Name: "Init"
In die SUB schreiben wir nun Quellcode, damit der Hintergrund des Spielfelds gezeichnet wird und verwenden den BackBuffer als aktuelles Hintergrundbild (man kann stattdessen natürlich auch einfach ein Bild laden).
Danach setzen wir die Position der Schläger auf die Randkoordinaten.
// ------------------------------------------------------------- //
// -=#  INIT  #=-
// ------------------------------------------------------------- //
SUB Init:
    // Spielfeld zeichnen, als Hintergrund nutzen
    BLACKSCREEN
    DRAWRECT 0, 0, 639, 15,  RGB(255, 255, 255)
    DRAWRECT 0, 464, 640, 16, RGB(255, 255, 255)
    DRAWRECT 312, 0, 16, 480, RGB(255, 255, 255)
    USEASBMP
    // Schläger 0 setzten
    bat_y[0]=240; bat_y[1]=240
    // Schläger 1 setzten
    bat_x[0]=16; bat_x[1]=600
ENDSUB // INIT
Ball zurücksetzen
Zu Begin jedes Spiels muss der Ball wieder in die Mitte platziert werden, und seine Geschwindigkeit wieder auf 1 gesetzt werden. Dazu fügen wir eine neue SUB ein, wie vorher beschrieben, und nennen Sie: ResetBall
// ------------------------------------------------------------- //
// -=#  RESETBALL  #=-
// ------------------------------------------------------------- //
SUB ResetBall: 
    ball_x=320
    ball_y=240
    IF ball_sx<0 // Wenn Ball nach links, dann jetzt nach rechts und vice versa
        ball_sx=1
    ELSE
        ball_sx=-1
    ENDIF
    ball_sy=1
ENDSUB // RESETBALL
Diese Funktion rufen wir auch in Init auf. Dazu in die SUB Init
GOSUB ResetBall
einfügen.
Anzeigen
Jetzt haben wir schon einiges programmiert, aber noch immer nichts gesehen. Es wird Zeit, dass wir das Spielfeld und den Ball anzeigen. Also, neue SUB mit Namen ShowAll:
// ------------------------------------------------------------- //
// -=#  SHOWALL  #=-
// ------------------------------------------------------------- //
SUB ShowAll:
    // Die Schläger anzeigen
    FOR num=0 TO 1
        DRAWRECT bat_x[num], bat_y[num], 16, 64, RGB(255, 255, 255)
        PRINT score[num], num*320 + 32, 16
    NEXT
    // Der Ball
    DRAWRECT ball_x, ball_y, 16, 16, RGB(255, 255, 255)
    SHOWSCREEN
ENDSUB // SHOWALL
Hauptschleife
Jetzt brauchen wir eine Hauptschleife, die immer wieder die SUBs aufruft. Dazu die folgenden Zeilen über die erste SUB schreiben. Vor der ersten SUB oder FUNCTION steht das Hauptprogramm. Dieses wird beim Programmstart ausgeführt.
// Voreinstellungen
GOSUB Init
// Hauptschleife
main:
    GOSUB ShowAll
GOTO main
Achtung! Der Code würde das Programm dazu bringen, dass es nicht mehr reagiert, wenn wir nicht in ShowAll den Befehl
SHOWSCREEN
aufrufen würden.
Der erste Start
Drücke jetzt den Knopf zum Kompilieren drücken und starte dann das Programm.
(F8, dann F5)
Bewegung!
Jetzt muss nur noch Bewegung in das Spiel. Dazu legen wir eine SUB mit dem Namen MoveAll an und fügen den Aufruf vor
GOSUB ShowAll
mit
GOSUB MoveAll
// ------------------------------------------------------------- //
// -=#  MOVEALL  #=-
// ------------------------------------------------------------- //
SUB MoveAll:
    // Schläger
    FOR num=0 TO 1
        // Spieler 1: Tasten: A, Y
        IF KEY(30) THEN bat_y[0]=bat_y[0]-2
        IF KEY(44) THEN bat_y[0]=bat_y[0]+2
        // Tasten: auf, ab
        IF KEY(200) THEN bat_y[1]=bat_y[1]-2
        IF KEY(208) THEN bat_y[1]=bat_y[1]+2
    
        // Schläger am oberen/unteren Rand?            IF bat_y[num]<0   THEN bat_y[num]=0
        IF bat_y[num]>416 THEN bat_y[num]=416
    NEXT
Die Codes für den KEY()-Befehl findest Du unter dem Menüpunkt "Werkzeuge/KeyCodes".
Jetzt kannst Du das Programm wieder kompilieren und starten. Mit den Tasten "A, Y" und "Auf, Ab" kann man jetzt beide Schläger steuern.
Nun bewegen wir den Ball um die aktuelle x/y Geschwindigkeit des Balls:
// Ball
ball_x=ball_x+ball_sx
ball_y=ball_y+ball_sy
Kollision mit Rand
Wenn der Ball oben oder unten anstößt, drehen wir einfach seine y-Geschwindigkeit um:
// Ball unten am Rand
IF ball_y>464
    ball_y=464
    ball_sy=-ball_sy
ENDIF
// Ball oben am Rand
IF ball_y<0
    ball_y=0
    ball_sy=-ball_sy
ENDIF
Wenn der Ball links/rechts an den Rand stößt, bekommt ein Spieler einen Punkt und der Ball wird wieder in die Mitte gesetzt.
// Ball linker Rand -> Punkt für Spieler 1
IF ball_x<0
    score[1]=score[1]+1
    GOSUB ResetBall
ENDIF
// Ball rechter Rand -> Punkt für Spieler 0
IF ball_x>624
    score[0]=score[0]+1
    GOSUB ResetBall
ENDIF
Kollision mit Schläger
Wir durchlaufen eine Schleife für alle Spieler:
FOR num=0 TO 1
Zunächst fragen wir ab, ob sich der Ball in Richtung des Schlägers bewegt. Wenn der Ball nämlich schon umgedreht wurde, kann er evtl. trotzdem noch mit dem Schläger kollidieren. Dadurch würde er sonst wieder auf die eigene Seite bewegt werden.
IF (ball_sx<0 AND num = 0) OR (ball_sx>0 AND num=1)
Ist dies der Fall, prüfen wir, ob sich die beiden Rechtecke Ball + Schläger überlappen:
Wenn der Ball an einen Schläger kommt, wird seine X-Geschwindigkeit umgedreht. Die Geschwindigkeiten in X und Y werden beide erhöht. Jedoch nicht im gleichen Maße, weil sich sonst der Flugwinkel des Balls nicht ändern würde.
col = BOXCOLL(bat_x[num], bat_y[num], 16, 64,   ball_x, ball_y, 16, 16)
    IF col=TRUE
    // Ballgeschwindigkeit in X umdrehen
    ball_sx= -ball_sx
    // Schneller machen / Speed up
    ball_sx = ball_sx * 1.2
    ball_sy = ball_sy * 1.05
Jetzt noch unsere IF Bedingungen und die Schleife abschließen:
        ENDIF
    ENDIF
NEXT
Voila. Fertig ist das Spiel.
Überprüfe alle Programmteile sorgfältig und verstehe jede einzelne Zeile des Codes, bevor Du versuchst ein eigenes Spiel zu schreiben. Sieh' die Referenz der Befehle in dieser Hilfe nach und versuche das Spiel auf 4 Spieler zu erweitern (oben und unten noch ein Schläger).
Erstelle einen Computer-Spieler, so dass Du auch allein spielen kannst. Dabei musst Du nur prüfen, ob die Y-Koordinate des Balls höher oder tiefer als der Mittelpunkt des Schlägers liegt, und dann den Schläger evtl. auf oder ab bewegen.
Oder versuche, dass je nach Aufschlagsposition des Balls der Flugwinkel geändert wird.
Das Programm lässt viel Platz für eigene Ideen.
Viel Spass beim Basteln.
Gesamter Quellcode:
// Pong
DIM bat_y[2]
DIM bat_x[2]
DIM score[2]
// Voreinstellungen / Setup values
GOSUB Init
// Hauptschleife / Main Loop
main:
    GOSUB MoveAll
    GOSUB ShowAll
GOTO main
// ------------------------------------------------------------- //
// -=#  INIT  #=-
// ------------------------------------------------------------- //
SUB Init:
    GOSUB ResetBall
    // Spielfeld zeichnen, als Hintergrund nutzen
    // Draw playfield, use as background bitmap
    BLACKSCREEN
   DRAWRECT 0,   0, 640, 15,  RGB(255, 255, 255)
    DRAWLRECT 0, 464, 640, 16, RGB(255, 255, 255)
    DRAWRECT 312, 0, 16, 479, RGB(255, 255, 255)
    USEASBMP
    // Schläger 0 setzten / Reset Bat 0
    bat_y[0]=240; bat_y[1]=240
    // Schläger 1 setzten / Reset Bat 1
    bat_x[0]=16; bat_x[1]=600
    
    // Klassischer Zeichensatz / Classic Font
    LOADFONT "FontBW.bmp", 0
ENDSUB // INIT
// ------------------------------------------------------------- //
// -=#  RESETBALL  #=-
// ------------------------------------------------------------- //
SUB ResetBall: 
    // Diese Variablen sind als LOCAL definiert:
    // These variables are defined LOCAL:
    // void
    ball_x=320
    ball_y=240
    IF ball_sx<0
        ball_sx=1
    ELSE
        ball_sx=-1
    ENDIF
    ball_sy=1
ENDSUB // RESETBALL
// ------------------------------------------------------------- //
// -=#  SHOWALL  #=-
// ------------------------------------------------------------- //
SUB ShowAll:
    // Die Schläger anzeigen / Show the bats
    FOR num=0 TO 1
        DRAWRECT bat_x[num], bat_y[num], 16, 64, RGB(255, 255, 255)
        PRINT score[num], num*320 + 32, 16
    NEXT
    // Der Ball / The ball
    DRAWRECT ball_x, ball_y, 16, 16, RGB(255, 255, 255)
    SHOWSCREEN
ENDSUB // SHOWALL
// ------------------------------------------------------------- //
// -=#  MOVEALL  #=-
// ------------------------------------------------------------- //
SUB MoveAll:
// Paddles
    FOR num=0 TO 1
        // Tasten / Keys:  A, Y
  IF KEY(30) THEN bat_y[0]=bat_y[0]-2
  IF KEY(44) THEN bat_y[0]=bat_y[0]+2
  // Tasten / Keys:  /\, \/
  IF KEY(200) THEN bat_y[1]=bat_y[1]-2
  IF KEY(208) THEN bat_y[1]=bat_y[1]+2
 
  // Schläger am Rand? / Bat at border?
  IF bat_y[num]<0   THEN bat_y[num]=0
  IF bat_y[num]>416 THEN bat_y[num]=416
 NEXT
// Ball
 ball_x=ball_x+ball_sx
 ball_y=ball_y+ball_sy
 
 // Ball unten am Rand / Ball at lower border
 IF ball_y>464
  ball_y=464
  ball_sy=-ball_sy
 ENDIF
 
 // Ball oben am Rand / Ball at upper border
 IF ball_y<0
  ball_y=0
  ball_sy=-ball_sy
 ENDIF
 
 // Ball Linker Rand / Ball at left border
 IF ball_x<0
  score[1]=score[1]+1
  GOSUB ResetBall
 ENDIF
 
 // Ball rechter Rand / Ball at right border
 IF ball_x>624
  score[0]=score[0]+1
  GOSUB ResetBall
 ENDIF
 
 // Pong
 FOR num=0 TO 1 // Für jeden Spieler / For each player
  // Bewegt sich der Ball auf den Schläger 'num' zu?
  // Does ball move toward bat 'num'
  IF (ball_sx<0 AND num = 0) OR (ball_sx>0 AND num=1)
   col=BOXCOLL(bat_x[num], bat_y[num], 16, 64,   ball_x, ball_y, 16, 16)
 
   IF col=TRUE
    // Ballgeschwindigkeit in X umdrehen / Flip ball's X-direction
    ball_sx= -ball_sx
    // Schneller machen / Speed up
    ball_sx = ball_sx * 1.2
    ball_sy = ball_sy * 1.05
   ENDIF
  ENDIF
 NEXT
 
ENDSUB // MOVEALL

