GLBasic Benutzerhandbuch

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
ein.


// ------------------------------------------------------------- //
// -=# 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

See also...