SDL: Timer

Aus Wikibooks

Timer[Bearbeiten]

Die API von SDL enthält einige Funktionen, die sich mit der Zeit beschäftigen. Es sind Funktionen enthalten, um nach dem Ablauf einer bestimmten Zeit eine Aktion durchzuführen, etwas Zeit verstreichen zu lassen und die Zeit seit Programmstart abzufragen. Um die Timer-API benutzen zu können, muss SDL_Init mit SDL_INIT_TIMER aufgerufen werden.


Timer-API[Bearbeiten]

Diese Funktionen sind:

  • Abfrage der Zeit seit Programmstart:
 Uint32 SDL_GetTicks (void);
  • Zeit verstreichen lassen:
 void SDL_Delay (Uint32 zeit_ms);

Die Zeit wird in Millisekunden angegeben

  • Starten eines Timers:
 SDL_TimerID SDL_AddTimer (Uint32 intervall_ms, SDL_NewTimerCallback funky, void *parameter);

Diese Funktion startet nach einer Zeit intervall_ms eine Funktion funky, der ein Parameter parameter als Argument übergeben werden kann. Die Zeit ist wieder in Millisekunden anzugeben. Die Callback-Funktion ist vom Typ:

 typedef Uint32 (*SDL_NewTimerCallback) (Uint32 intervall, void *parameter);

Der Parameter von SDL_AddTimer wird direkt an die Callback-Funktion übergeben. Wenn die Funktion 0 zurückgibt, dann wird der Timer gelöscht, ansonsten gibt der Rückgabewert des Callbacks den neuen Intervall an.

  • Einen Timer wieder löschen:
 SDL_bool SDL_RemoveTimer (SDL_TimerID timer);

Entfernt den angegebenen Timer.

Bouncing Box[Bearbeiten]

Bei Bouncing-Box geht es darum, ein farbiges Quadrat in einem Rechteck entlanglaufen zu lassen. Stößt dieses Rechteck an eine Wand, so wird es von dieser reflektiert. Das Programm endet, wenn die Escape-Taste gedrückt wurde. Hierzu benutzen wir die Timer API.

 timer = SDL_AddTimer (10, meine_funk, NULL);

Nach jeweils 10ms wird die Funktion meine_funk aufgerufen, die ihrerseits ein SDL_USEREVENT generiert und in die Event-Loop einfügt. Alle 10ms wird innerhalb der Ereignisschleife also folgender Programmcode

 SDL_FillRect (screen, &rect, SDL_MapRGB (screen->format, 0, 0, 0));
 SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
 move_rect (&rect, &pos);
 SDL_FillRect (screen, &rect, SDL_MapRGB (screen->format, 255, 0, 0));
 SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);

ausgeführt. Es wird die alte Position des Rechteckes gelöscht, dann wird eine neue Position berechnet und dort ein rotes Kästchen gemalt. Die Funktion move_rect berechnet die neue Position, wobei darauf zu achten ist, dass Zusammenstöße mit den Wänden besonders behandelt werden.

Programmcode[Bearbeiten]

 #include <stdlib.h>
 #include <SDL.h>
 
 /* Punkte auf der Ebene */
 typedef struct _Pos
 {
   double x, y, ex, ey, v;
 } Pos;
 
 void move_rect (SDL_Rect *r, Pos *p)
 {
   double nx, ny;
   double pgx0, pgx1, pgy0, pgy1;
 
   /* Groesse des Spielfeldes */
   pgx0 = r->w;
   pgx1 = 640 - r->w;
   pgy0 = r->h;
   pgy1 = 480 - r->h;
 
   /* Neue Position */
   nx = p->x + p->v * p->ex;
   ny = p->y + p->v * p->ey;
 
   /* stossen wir links oder rechts an? */
   if (nx <= pgx0)
     {
       p->ex = -p->ex;
       nx = pgx0 + (pgx0 - nx);
     }
   else if (nx >= (pgx1))
     {
       p->ex = -p->ex;
       nx = pgx1 - (nx - pgx1);
     }
 
   /* Stossen wir oben oder unten an? */
   if (ny <= pgy0)
     {
       p->ey = -p->ey;
       ny = pgy0 + (pgy0 - ny);
     }
   else if (ny >= (pgy1))
     {
       p->ey = -p->ey;
       ny = pgy1 - (ny - pgy1);
     }
 
   /* neue Position und Zeichenposition übergeben */
   p->x = nx;
   p->y = ny;
   r->x = nx - r->w / 2;
   r->y = ny - r->h / 2;
 }
 
 Uint32 meine_funk (Uint32 intervall, void *parameter)
 {
   SDL_Event event;
   SDL_UserEvent userevent;
 
   /* initialisiere einen UserEvent */
   userevent.type = SDL_USEREVENT;
   userevent.code = 0;
   userevent.data1 = NULL;
   userevent.data2 = NULL; 
 
   /* ... initialisiere damit einen Event */
   event.type = SDL_USEREVENT;
   event.user = userevent;
 
   /* neuen Event erzeugen */
   SDL_PushEvent (&event);
   return intervall;
 }
 
 int main (void) 
 {
   SDL_Surface *screen;
   SDL_Event e;
   SDL_TimerID timer;
   SDL_Rect rect;
   Pos pos;
   int quit = 0;
 
   if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_TIMER) == -1)
     {
       printf ("Kann SDL nicht initialisieren: %s\n", SDL_GetError ());
       exit (1);
     }
   atexit (SDL_Quit);
   screen = SDL_SetVideoMode (640, 480, 16, SDL_SWSURFACE);
   if (screen == NULL)
     {
       printf ("Video Modus kann nicht eingerichtet werden: %s\n", SDL_GetError());
       exit (1);
     }
 
   rect.x = 100;
   rect.y = 100;
   rect.w = 10;
   rect.h = 10;
 
   pos.x = 100.0;
   pos.y = 100.0;
   pos.ex = 0.7;
   pos.ey = 0.7;
   pos.v = 4.31;
 
   /* Den Timer auf 10ms stellen und starten */
   timer = SDL_AddTimer (10, meine_funk, NULL);
 
   while (SDL_WaitEvent (&e) && !quit)
     switch (e.type)
       {
         case SDL_KEYDOWN:
           if (e.key.keysym.sym == SDLK_ESCAPE)
             quit = 1;
         case SDL_USEREVENT:
           {
             SDL_FillRect (screen, &rect, SDL_MapRGB (screen->format, 0, 0, 0));
             SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
             move_rect (&rect, &pos);
             SDL_FillRect (screen, &rect, SDL_MapRGB (screen->format, 255, 0, 0));
             SDL_UpdateRect(screen, rect.x, rect.y, rect.w, rect.h);
           }
         break;
       }
   return 0;
 }