Pong

Überlegung

An diesem Punkt, oder eventuell schon früher, müssen wir uns überlegen, ob es über Tastatur möglich ist, beide Spieler zu steuern, oder ob eine Art KI1) dies übernimmt. In diesem Tutorial werde ich der Einfachheit halber ein 2-Spieler Pong schreiben, ohne KI(das kann dann jeder für sich selbst zum Üben machen:-)).
Also heißt das, dass beide Spieler mittels der Tastatur bewegt werden müssen. Dafür bietet uns SDL etwas, dass sich SDL_Event nennt.

SDL_Events laden

Wie man in unserer SDL Vorlage bereits sieht, gibt es einen Abschnitt der eine Variable namens event vom Type SDL_Event deklariert. Die Adresse dieser Variable wird der Funktion SDL_PollEvent() als Parameter übergeben und anschließend wird mit ihr gearbeitet. Für uns ist der Teil innerhalb switch( event.type ) wichtig. Denn dort gibt es neben SDL_Quit auch SDL_KEYDOWN und danach suchen wir!

SDL_KEYDOWN

Aber bevor wir unsere Spieler bewegen können, müssen wir definieren wieviel Pixel ein Schritt zurücklegt. Dafür gehen wir wieder in die pong.h und definieren:

#define STEP 12 // wieviele Pixel legt 1 Schritt zurück

Und fügen wir innerhalb des switch( event.type ) folgendes hinzu:

// check for keypresses
case SDL_KEYDOWN:
{
    // exit if ESCAPE is pressed
    if( event.key.keysym.sym == SDLK_ESCAPE )
    {
        done = true;
        break;
     }
 
    // arrow key pressed ( right player//pong )
    if( event.key.keysym.sym == SDLK_UP )
    { // up means negative y
        if( (pong.get_position())->y - STEP >= DEFAULT_GAP )
        { // die Bewegung bleibt innerhalb des screens
            pong.update_position( 0 - STEP );
            update_sprites = true;
        }
    }
    if( event.key.keysym.sym == SDLK_DOWN )
    { // down means positive y
        if( (pong.get_position())->y + STEP + pong.bmp->h <= ( SCREEN_HEIGHT - DEFAULT_GAP ) )
        { // die Bewegung bleibt innerhalb des screens
            pong.update_position( STEP );
            update_sprites = true;
        }
    }
 
    // if W or S key pressed( left player//ping )
    if( event.key.keysym.sym == SDLK_w )
    { // up means negative
        if( (ping.get_position())->y - STEP >= DEFAULT_GAP )
        { // die Bewegung bleibt innerhalb des screens
            ping.update_position( 0 - STEP );
            update_sprites = true;
        }
    }
    if( event.key.keysym.sym == SDLK_s )
    { // down means positive y
        if( (ping.get_position())->y + STEP <= ( SCREEN_HEIGHT - DEFAULT_GAP ) )
        { // die Bewegung bleibt innerhalb des screens
            ping.update_position( STEP );
            update_sprites = true;
        }
    }
}
    break; // für den switch

Nun bewegen sich die Spieler beim Drücken. Es fällt aber etwas auf: Man kann die Tasten nicht gedrückt halten. Dies fühlt sich unnatürlich und unschön an. Aber dies lässt sich ändern. Dafür müssen wir zusätzlich 2 Variablen sowie das SDLK_KEYUP Event verwenden.

SDL_KEYUP

SDL_KEYUP macht das, was der Name bereits vermuten lässt. Wenn das Event in unserer EventPoll ist, dann wurde eine Taste losgelassen. Nun müssen wir eine Variable setzen, wenn der Spieler 1 eine Taste drückt und eine Variable, wenn der Spieler 2 eine Taste drückt. Zuerst einmal sollten wir diese deklarieren. Dafür gehen wir vor den while( !done ) Loop und schreiben:

signed char keypress_1 = 0;
signed char keypress_2 = 0;

Und bevor wir jetzt das Tastendrücken weiter editieren, müssen wir wieder einige Dinge definieren. Denn würden wir jetzt einfach weiterschreiben, würde in jedem Loop solange die Taste gedrückt gehalten wird, sich der Spieler bewegen. Das wäre viel zu schnell. Deshalb definieren wir in der pong.h:

#define TICK_TO_SEC 1000 // 1000 ticks are 1 second -- don't change
#define STEPS_PER_SEC 20 // when key is hold down, how often per sec does the player step

Und anhand dieser Definitionen werdet ihr erkennen, dass wir nun mit Zeit arbeiten. Und deshalb brauchen wir 2 weitere Variablen. Diese bitte in der main.cpp außerhalb der main() Funktion wie folgt deklarieren:

static unsigned long int move_ping_again = 0;
static unsigned long int move_pong_again = 0;

Diese Variablen werden speichern, wann der nächste Schritt unserer Spieler ist.
Zusätzlich würde eine Variable, in welcher wir immer die Zeit speichern können, der Übersicht beitragen. Deshalb wird am Anfang, aber innerhalb, der while( !done ) Schleife folgende deklariert:

unsigned long int now;

So, nun zurück zum SDL_KEYDOWN:

// arrow key pressed
if( event.key.keysym.sym == SDLK_UP || event.key.keysym.sym == SDLK_DOWN )
{
 
   if( keypress_2 != 0 ) // already pressed down one key
        break;
 
   if( event.key.keysym.sym == SDLK_UP && event.key.keysym.sym == SDLK_DOWN ) // pressing down both at once
        break;
 
   if( event.key.keysym.sym == SDLK_UP )
   {
        keypress_2 = -1; // moving up
    }
    else
    {
         keypress_2 = 1; // moving down
     }
}
// W(A)S(D) key pressed
if( event.key.keysym.sym == SDLK_w || event.key.keysym.sym == SDLK_s )
{
 
    if( keypress_1 != 0 ) // already pressed down one key
        break;
 
    if( event.key.keysym.sym == SDLK_w && event.key.keysym.sym == SDLK_s ) // pressing down both at once
        break;
 
    if( event.key.keysym.sym == SDLK_w )
    {
        keypress_1 = -1;
    }
    else
    {
        keypress_1 = 1;
    }
}
break; // case SDL_KEYDOWN
 
// check for keyreleases
case SDL_KEYUP:
    if( event.key.keysym.sym == SDLK_w || event.key.keysym.sym == SDLK_s )
        keypress_1 = 0;
    if( event.key.keysym.sym == SDLK_UP || event.key.keysym.sym == SDLK_DOWN )
        keypress_2 = 0;
    break;
} // end of switch
} // end of message processing
 
now = SDL_GetTicks();
if( keypress_1 != 0 && move_ping_again <= now )
    {
        if( keypress_1 > 0 && ( (ping.get_position())->y + DEFAULT_GAP + ping.bmp->h ) <= SCREEN_HEIGHT )
            ping.update_position( STEP );
        else if( keypress_1 < 0 && (ping.get_position())->y >= DEFAULT_GAP )
            ping.update_position( 0 - STEP );
        move_ping_again = SDL_GetTicks() + ( TICK_TO_SEC / STEPS_PER_SEC );
        update_sprites = true;
    }
if( keypress_2 != 0 && move_pong_again <= now )
    {
        if( keypress_2 > 0 && ( (pong.get_position())->y + DEFAULT_GAP + ping.bmp->h ) <= screen->h )
            pong.update_position( STEP );
        else if( keypress_2 < 0 && (pong.get_position())->y - STEP >= DEFAULT_GAP )
            pong.update_position( 0 - STEP );
        move_pong_again = SDL_GetTicks() + ( TICK_TO_SEC / STEPS_PER_SEC );
        update_sprites = true;
        }

Nun sollten sich unere Spieler beim Gedrückthalten automatisch weiterbewegen. Also nächstes müssen wir dafür sorgen, dass sich auch der Ball bewegt.

Der Spielball

1)
künstliche Intelligenz