sdl初學筆記

目錄

1.介紹

例子1:初始化

2.圖形和視頻

2.1SDL的視頻介紹

2.1.1初始化視頻顯示

例子2——1 初始化視頻顯示

2.1.2初始化最好的視頻模式

例子2——2 初始化最好的視頻模式

2.1.3讀取和顯示一個bmp 文件

例子2——3 讀取和現實一個bmp 文件

2.1.4直接繪圖來顯示

例子2——4getpixel()

例子2——6 使用putpixel()

2.2在sdl中使用opengl

2.2.1初始化

例子2——7 初始化SDL 的opengl

2.2.2繪製

例子2——8SDL 和OpenGL

3 輸入處理

3.1控制桿處理

3.1.1初始化

3.1.2詢問

3.1.3打開一個控制桿接受控制桿事件

3.1.4優化的控制桿功能

3.2處理鍵盤

3.2.1鍵盤相關結構體

3.2.2SDLKey

3.2.4SDL_keysym

3.2.5SDL_KeyboardEvent

3.2.6讀取鍵盤事件

3.2.7一個更加詳細的查看

3.2.8遊戲模式輸入


1.介紹

SDL 是一個由8 個子系統組成——音頻、CDROM、事件驅動、文件I/O、操作杆驅動、線程、時鐘和視頻。在你使用這些子系統之前,你必須要初始化他們,使用SDL_Init(或者SDL_InitSubSystem。SDL_Init 必須在其他SDL 功能被調用之前調用。所以初始化默認的子系統和視頻子系統你需要調用SDL_Init(SDL_INIT_VIDEO),而初始化默認的子系統、視頻系統和時間系統你需要調用SDL_Init ( SDL_INIT_VIDEO | SDL_INIT_TIMER ))SDL_Init 的退出使用SDL_Quit(和SDL_QuitSubSystem)SDL_Quit 關掉所有子系統,包括默認的,它應該被調用在SDL 應用退出之前。
SDL_Init 和SDL_Quit 堅固的嵌入你的程序工具箱,你能寫你的第一個和最基礎的SDL應用。但是,我們必須準備處理錯誤。許多SDL 功能返回一個值指出這個功能是成功或者失敗,例如,SDL_Init 返回-1 如果不能初始化一個子系統。SDL 提供一個有用的設備允許你精確的定位錯誤的類型, 每次一個SDL 內錯誤發生一個錯誤信息被存儲, 使用SDL_GetError 可以找到。經常使用這個,你能知道錯誤的很多。

例子1:初始化

#include "SDL.h" /* All SDL App's need this */
#include <stdio.h>

int main() 
{
    printf("Initializing SDL.\n");
    /* Initialize defaults, Video and Audio */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)==-1)) 
	{
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }
    printf("SDL initialized.\n");
    printf("Quiting SDL.\n");
    /* Shutdown all subsystems */
    SDL_Quit();
    printf("Quiting....\n");
    exit(0);
}

編譯:

$ gcc demo-init.c `pkg-config --libs --cflags sdl`
$ ./a.out 
Initializing SDL.
SDL initialized.
Quiting SDL.
Quiting....

2.圖形和視頻


2.1SDL的視頻介紹


視頻也許是最常用的SDL 的應用地方,所以它有最全套的子系統。這裏是一些例子來顯示這個基礎。


2.1.1初始化視頻顯示


例子2——1 初始化視頻顯示

#include "SDL.h" /* All SDL App's need this */
#include <stdio.h>

int main() 
{
    printf("Initializing SDL.\n");
    /* Initialize defaults, Video and Audio */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)==-1)) 
	{
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }
    printf("SDL initialized.\n");
    
    SDL_Surface *screen;
        
    /* Clean up on exit */
    atexit(SDL_Quit);
    /*
     * Initialize the display in a 640x480 8-bit palettized mode,
     * requesting a software surface
     */
    screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
    if ( screen == NULL ) 
	{
        fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
        SDL_GetError());
        exit(1);
    }

    printf("Quiting SDL.\n");
    /* Shutdown all subsystems */
    SDL_Quit();
    printf("Quiting....\n");
    exit(0);
}

顯示一個:

2.1.2初始化最好的視頻模式


如果你有一個偏好一個可靠的像素深度但是將要接受其他的, 那麼如下使用SDL_SetVideoMode 和SDL_ANYFORMAT。你也能使用SDL_VideoModeOK() 來找到當地視頻模式來儘量匹配你要求的模式。


例子2——2 初始化最好的視頻模式

#include "SDL.h" /* All SDL App's need this */
#include <stdio.h>

int main() 
{
    printf("Initializing SDL.\n");
    /* Initialize defaults, Video and Audio */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)==-1)) 
	{
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }
    printf("SDL initialized.\n");
    
    SDL_Surface *screen;
        
    /* Clean up on exit */
    atexit(SDL_Quit);
    
    /* Have a preference for 8-bit, but accept any depth */
    screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE|SDL_ANYFORMAT);
    if ( screen == NULL ) {
        fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
        SDL_GetError());
        exit(1);
    }
    printf("Set 640x480 at %d bits-per-pixel mode\n",
    screen->format->BitsPerPixel);

    printf("Quiting SDL.\n");
    /* Shutdown all subsystems */
    //SDL_Quit();
    printf("Quiting....\n");
    exit(0);
}

2.1.3讀取和顯示一個bmp 文件


下面的功能讀取和顯示一個作爲參數給出的bmp 文件,前提SDL 被初始化視頻模式被設置。


例子2——3 讀取和現實一個bmp 文件

#include "SDL.h" /* All SDL App's need this */
#include <stdio.h>

void display_bmp(char *file_name)
{
    SDL_Surface *image;
    SDL_Surface *screen;
    
    /* Load the BMP file into a surface */
    image = SDL_LoadBMP(file_name);
    if (image == NULL) {
        fprintf(stderr, "Couldn't load %s: %s\n", file_name, SDL_GetError());
        return;
    }
    /* Have a preference for 8-bit, but accept any depth */
    screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE|SDL_ANYFORMAT);
    if ( screen == NULL ) {
        fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
        SDL_GetError());
        exit(1);
    }
    printf("Set 640x480 at %d bits-per-pixel mode\n",
        screen->format->BitsPerPixel);
        
    /*
    * Palettized screen modes will have a default palette (a standard
    * 8*8*4 colour cube), but if the image is palettized as well we can
    * use that palette for a nicer colour matching
    */
    if (image->format->palette && screen->format->palette) {
        SDL_SetColors(screen, image->format->palette->colors, 0,
        image->format->palette->ncolors);
    }
    /* Blit onto the screen surface */
    if(SDL_BlitSurface(image, NULL, screen, NULL) < 0)
        fprintf(stderr, "BlitSurface error: %s\n", SDL_GetError());
    
    SDL_UpdateRect(screen, 0, 0, image->w, image->h);
    
    /* Free the allocated BMP surface */
    SDL_FreeSurface(image);
    
    sleep(2);
}

int main() 
{
    printf("Initializing SDL.\n");
    /* Initialize defaults, Video and Audio */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)==-1)) 
	{
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }
    printf("SDL initialized.\n");
    
    SDL_Surface *screen;

    display_bmp("a.bmp");
    
    printf("Quiting SDL.\n");
    /* Shutdown all subsystems */
    //SDL_Quit();
    printf("Quiting....\n");
    exit(0);
}

結果運行時會顯示一個圖片到屏幕上,顯示兩秒鐘。

2.1.4直接繪圖來顯示


以下的兩個功能能夠用於得到和設置單獨的一個平面的像素。他們仔細的被寫和任何當前SDL 的深度工作。基礎要鎖定這個表面再調用他們,在調用其他SDL 功能之前需要解鎖。

在像素值和他們的紅、綠、藍部件之間轉換,使用SDL_GetRGB() 和SDL_MapRGB()。


例子2——4getpixel()

#include "SDL.h" /* All SDL App's need this */
#include <stdio.h>

/*
* Return the pixel value at (x, y)
* NOTE: The surface must be locked before calling this!
*/
Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to retrieve */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
    switch(bpp) {
        case 1:
            return *p;
        case 2:
            return *(Uint16 *)p;
        case 3:
            if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
                return p[0] << 16 | p[1] << 8 | p[2];
            else
                return p[0] | p[1] << 8 | p[2] << 16;
        case 4:
            return *(Uint32 *)p;
        default:
            return 0; /* shouldn't happen, but avoids warnings */
    }
}

int main() 
{
    printf("Initializing SDL.\n");
    /* Initialize defaults, Video and Audio */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)==-1)) 
	{
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }
    printf("SDL initialized.\n");
    
    SDL_Surface *screen;

    /* Have a preference for 8-bit, but accept any depth */
    screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE|SDL_ANYFORMAT);
    if ( screen == NULL ) {
        fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
        SDL_GetError());
        exit(1);
    }
    printf("Set 640x480 at %d bits-per-pixel mode\n",
        screen->format->BitsPerPixel);
        
    getpixel(screen, 12, 12);
    
    printf("Quiting SDL.\n");
    /* Shutdown all subsystems */
    SDL_Quit();
    printf("Quiting....\n");
    exit(0);
}

下面的代碼使用putpixel()功能來設置一個黃色像素在屏幕中間


例子2——6 使用putpixel()

#include "SDL.h" /* All SDL App's need this */
#include <stdio.h>

/*
* Return the pixel value at (x, y)
* NOTE: The surface must be locked before calling this!
*/
void putpixel(SDL_Surface *surface, int x, int y, Uint32 color)
{
    int bpp = surface->format->BytesPerPixel;
    /* Here p is the address to the pixel we want to retrieve */
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
    *p = color;
}

int main() 
{
    printf("Initializing SDL.\n");
    /* Initialize defaults, Video and Audio */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO)==-1)) 
	{
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }
    printf("SDL initialized.\n");
    
    SDL_Surface *screen;

    /* Have a preference for 8-bit, but accept any depth */
    screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE|SDL_ANYFORMAT);
    if ( screen == NULL ) {
        fprintf(stderr, "Couldn't set 640x480x8 video mode: %s\n",
        SDL_GetError());
        exit(1);
    }
    printf("Set 640x480 at %d bits-per-pixel mode\n",
        screen->format->BitsPerPixel);
        
    /* Code to set a yellow pixel at the center of the screen */
    int x, y;
    Uint32 yellow;
    /* Map the color yellow to this display (R=0xff, G=0xFF, B=0x00)
    Note: If the display is palettized, you must set the palette first.
    */
    yellow = SDL_MapRGB(screen->format, 0xff, 0xff, 0x00);
    x = screen->w / 2;
    y = screen->h / 2;
    /* Lock the screen for direct access to the pixels */
    if ( SDL_MUSTLOCK(screen) ) {
        if ( SDL_LockSurface(screen) < 0 ) {
            fprintf(stderr, "Can't lock screen: %s\n", SDL_GetError());
            return;
        }
    }
    putpixel(screen, x, y, yellow);
    if ( SDL_MUSTLOCK(screen) ) {
        SDL_UnlockSurface(screen);
    }
    
    /* Update just the part of the display that we've changed */
    SDL_UpdateRect(screen, x, y, 1, 1);
    
    sleep(4);

    printf("Quiting SDL.\n");
    /* Shutdown all subsystems */
    SDL_Quit();
    printf("Quiting....\n");
    exit(0);
}

2.2在sdl中使用opengl


SDL 有能力創建和使用opengl 環境在一系列的平臺上(Linux/X11,Win32,BeOs,MacOs,Classic/Toolbox,MacOs X,FreeBSD/X11 和Solaris/X11)。允許你使用SDL 的音頻,事件處理,線程和定時器在你的opengl 應用(一個功能經常被GLUT 執行)。


2.2.1初始化


初始化SDL 來使用opengl 是非常不同於正常的初始化SDL。有三個不同;你必須傳遞SDL_OPENGL 到SDL_SetVideoMode,你必須詳說明一系列GL 屬性(深度緩衝尺寸,幀緩衝尺寸)通過使用SDL_GL_SetAttribute 和最終的,如果你希望來使用“雙緩衝”你必須詳說明它作爲一個GL 屬性,不通過傳送SDL_DOUBLEBUF 標記到SDL_SetVideoMode


例子2——7 初始化SDL 的opengl

/*這個我沒有測試*/
/* Information about the current video settings. */
const SDL_VideoInfo* info = NULL;
/* Dimensions of our window. */
int width = 0;
int height = 0;
/* Color depth in bits of our window. */
int bpp = 0;
/* Flags we will pass into SDL_SetVideoMode. */
int flags = 0;
/* First, initialize SDL's video subsystem. */
if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
    /* Failed, exit. */
    fprintf( stderr, "Video initialization failed: %s\n",
    SDL_GetError( ) );
    quit_tutorial( 1 );
}
/* Let's get some video information. */
info = SDL_GetVideoInfo( );
if( !info ) {
    /* This should probably never happen. */
    fprintf( stderr, "Video query failed: %s\n",
    SDL_GetError( ) );
    quit_tutorial( 1 );
}
/*
* Set our width/height to 640/480 (you would
* of course let the user decide this in a normal
* app). We get the bpp we will request from
* the display. On X11, VidMode can't change
* resolution, so this is probably being overly
* safe. Under Win32, ChangeDisplaySettings
* can change the bpp.
*/
width = 640;
height = 480;
bpp = info->vfmt->BitsPerPixel;
/*
* Now, we want to setup our requested
* window attributes for our OpenGL window.
* We want *at least* 5 bits of red, green
* and blue. We also want at least a 16-bit
* depth buffer.
*
* The last thing we do is request a double
* buffered window. '1' turns on double
* buffering, '0' turns it off.
*
* Note that we do not use SDL_DOUBLEBUF in
* the flags to SDL_SetVideoMode. That does
* not affect the GL attribute state, only
* the standard 2D blitting setup.
*/
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
/*
* We want to request that SDL provide us
* with an OpenGL window, in a fullscreen
* video mode.
*
* EXERCISE:
* Make starting windowed an option, and
* handle the resize events properly with
* glViewport.
*/
flags = SDL_OPENGL | SDL_FULLSCREEN;

/*
* Set the video mode
*/
if( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {
    /*
    * This could happen for a variety of reasons,
    * including DISPLAY not being set, the specified
    * resolution not being available, etc.
    */
    fprintf( stderr, "Video mode set failed: %s\n",
    SDL_GetError( ) );
    quit_tutorial( 1 );
}

2.2.2繪製


相比初始化,在SDL 中使用opengl 與在其他api 中使用opengl 是相同的,e.g.GLUT。你還是使用所有同樣功能的調用和數據結構。但是如果你在使用一個雙緩衝顯示,你必須使用SDL_GL_GetAttribute 來看你是否真正的獲得了它。一個全面的例子顯示如下。


例子2——8SDL 和OpenGL

/*
* SDL OpenGL Tutorial.
* (c) Michael Vance, 2000
* [email protected]
*
* Distributed under terms of the LGPL.
*/
#include <SDL/SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <stdio.h>
#include <stdlib.h>

static GLboolean should_rotate = GL_TRUE;
static void quit_tutorial( int code )
{
    /*
    * Quit SDL so we can release the fullscreen
    * mode and restore the previous video settings,
    * etc.
    */
    SDL_Quit( );
    /* Exit program. */
    exit( code );
}
static void handle_key_down( SDL_keysym* keysym )
{
    /*
    * We're only interested if 'Esc' has
    * been presssed.
    *
    * EXERCISE:
    * Handle the arrow keys and have that change the
    * viewing position/angle.
    */
    switch( keysym->sym ) {
        case SDLK_ESCAPE:
            quit_tutorial( 0 );
            break;
        case SDLK_SPACE:
            should_rotate = !should_rotate;
            break;
        default:
            break;
    }
}
static void process_events( void )
{
    /* Our SDL event placeholder. */
    SDL_Event event;
    /* Grab all the events off the queue. */
    while( SDL_PollEvent( &event ) ) {
        switch( event.type ) {
            case SDL_KEYDOWN:
                /* Handle key presses. */
                handle_key_down( &event.key.keysym );
                break;
            case SDL_QUIT:
                /* Handle quit requests (like Ctrl-c). */
                quit_tutorial( 0 );
                break;
        }
    }
}
static void draw_screen( void )
{
    /* Our angle of rotation. */
    static float angle = 0.0f;
    /*
    * EXERCISE:
    * Replace this awful mess with vertex
    * arrays and a call to glDrawElements.
    *
    * EXERCISE:
    * After completing the above, change
    * it to use compiled vertex arrays.
    *
    * EXERCISE:
    * Verify my windings are correct here ;).
    */
    static GLfloat v0[] = { -1.0f, -1.0f, 1.0f };
    static GLfloat v1[] = { 1.0f, -1.0f, 1.0f };
    static GLfloat v2[] = { 1.0f, 1.0f, 1.0f };
    static GLfloat v3[] = { -1.0f, 1.0f, 1.0f };
    static GLfloat v4[] = { -1.0f, -1.0f, -1.0f };
    static GLfloat v5[] = { 1.0f, -1.0f, -1.0f };
    static GLfloat v6[] = { 1.0f, 1.0f, -1.0f };
    static GLfloat v7[] = { -1.0f, 1.0f, -1.0f };
    static GLubyte red[] = { 255, 0, 0, 255 };
    static GLubyte green[] = { 0, 255, 0, 255 };
    static GLubyte blue[] = { 0, 0, 255, 255 };
    static GLubyte white[] = { 255, 255, 255, 255 };
    static GLubyte yellow[] = { 0, 255, 255, 255 };
    static GLubyte black[] = { 0, 0, 0, 255 };
    static GLubyte orange[] = { 255, 255, 0, 255 };
    static GLubyte purple[] = { 255, 0, 255, 0 };
    /* Clear the color and depth buffers. */
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    /* We don't want to modify the projection matrix. */
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity( );
    /* Move down the z-axis. */
    glTranslatef( 0.0, 0.0, -5.0 );
    /* Rotate. */
    glRotatef( angle, 0.0, 1.0, 0.0 );
    if( should_rotate ) {
        if( ++angle > 360.0f ) {
            angle = 0.0f;
        }
    }
    /* Send our triangle data to the pipeline. */
    glBegin( GL_TRIANGLES );
    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( blue );
    glVertex3fv( v2 );
    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( blue );
    glVertex3fv( v2 );
    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( black );
    glVertex3fv( v5 );
    glColor4ubv( orange );
    glVertex3fv( v6 );
    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( orange );
    glVertex3fv( v6 );
    glColor4ubv( blue );
    glVertex3fv( v2 );
    glColor4ubv( black );
    glVertex3fv( v5 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( purple );
    glVertex3fv( v7 );
    glColor4ubv( black );
    glVertex3fv( v5 );
    glColor4ubv( purple );
    glVertex3fv( v7 );
    glColor4ubv( orange );
    glVertex3fv( v6 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( purple );
    glVertex3fv( v7 );
    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( blue );
    glVertex3fv( v2 );
    glColor4ubv( orange );
    glVertex3fv( v6 );
    glColor4ubv( white );
    glVertex3fv( v3 );
    glColor4ubv( orange );
    glVertex3fv( v6 );
    glColor4ubv( purple );
    glVertex3fv( v7 );
    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( red );
    glVertex3fv( v0 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( green );
    glVertex3fv( v1 );
    glColor4ubv( yellow );
    glVertex3fv( v4 );
    glColor4ubv( black );
    glVertex3fv( v5 );
    glEnd( );
    /*
    * EXERCISE:
    * Draw text telling the user that 'Spc'
    * pauses the rotation and 'Esc' quits.
    * Do it using vetors and textured quads.
    */
    /*
    * Swap the buffers. This this tells the driver to
    * render the next frame from the contents of the
    * back-buffer, and to set all rendering operations
    * to occur on what was the front-buffer.
    *
    * Double buffering prevents nasty visual tearing
    * from the application drawing on areas of the
    * screen that are being updated at the same time.
    */
    SDL_GL_SwapBuffers();
}
static void setup_opengl( int width, int height )
{
    float ratio = (float) width / (float) height;
    /* Our shading model--Gouraud (smooth). */
    glShadeModel( GL_SMOOTH );
    /* Culling. */
    glCullFace( GL_BACK );
    glFrontFace( GL_CCW );
    glEnable( GL_CULL_FACE );
    /* Set the clear color. */
    glClearColor( 0, 0, 0, 0 );
    /* Setup our viewport. */
    glViewport( 0, 0, width, height );
    /*
    * Change to the projection matrix and set
    * our viewing volume.
    */
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity( );
    /*
    * EXERCISE:
    * Replace this with a call to glFrustum.
    */
    gluPerspective( 60.0, ratio, 1.0, 1024.0 );
}


int main( int argc, char* argv[] )
{
    /* Information about the current video settings. */
    const SDL_VideoInfo* info = NULL;
    /* Dimensions of our window. */
    int width = 0;
    int height = 0;
    /* Color depth in bits of our window. */
    int bpp = 0;
    /* Flags we will pass into SDL_SetVideoMode. */
    int flags = 0;
    /* First, initialize SDL's video subsystem. */
    if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
        /* Failed, exit. */
        fprintf( stderr, "Video initialization failed: %s\n",
        SDL_GetError( ) );
        quit_tutorial( 1 );
    }
    /* Let's get some video information. */
    info = SDL_GetVideoInfo( );
    if( !info ) {
        /* This should probably never happen. */
        fprintf( stderr, "Video query failed: %s\n",
        SDL_GetError( ) );
        quit_tutorial( 1 );
    }
    /*
    * Set our width/height to 640/480 (you would
    * of course let the user decide this in a normal
    * app). We get the bpp we will request from
    * the display. On X11, VidMode can't change
    * resolution, so this is probably being overly
    * safe. Under Win32, ChangeDisplaySettings
    * can change the bpp.
    */
    width = 640;
    height = 480;
    bpp = info->vfmt->BitsPerPixel;
    /*
    * Now, we want to setup our requested
    * window attributes for our OpenGL window.
    * We want *at least* 5 bits of red, green
    * and blue. We also want at least a 16-bit
    * depth buffer.
    *
    * The last thing we do is request a double
    * buffered window. '1' turns on double
    * buffering, '0' turns it off.
    *
    * Note that we do not use SDL_DOUBLEBUF in
    * the flags to SDL_SetVideoMode. That does
    * not affect the GL attribute state, only
    * the standard 2D blitting setup.
    */
    SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
    SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
    /*
    * We want to request that SDL provide us
    * with an OpenGL window, in a fullscreen
    * video mode.
    *
    * EXERCISE:
    * Make starting windowed an option, and
    * handle the resize events properly with
    * glViewport.
    */
    flags = SDL_OPENGL | SDL_FULLSCREEN;
    /*
    * Set the video mode
    */
    if( SDL_SetVideoMode( width, height, bpp, flags ) == 0 ) {
        /*
        * This could happen for a variety of reasons,
        * including DISPLAY not being set, the specified
        * resolution not being available, etc.
        */
        fprintf( stderr, "Video mode set failed: %s\n",
        SDL_GetError( ) );
        quit_tutorial( 1 );
    }
    /*
    * At this point, we should have a properly setup
    * double-buffered window for use with OpenGL.
    */
    setup_opengl( width, height );
    /*
    * Now we want to begin our normal app process--
    * an event loop with a lot of redrawing.
    */
    while( 1 ) {
        /* Process incoming events. */
        process_events( );
        /* Draw the screen. */
        draw_screen( );
    }
    /*
    * EXERCISE:
    * Record timings using SDL_GetTicks() and
    * and print out frames per second at program
    * end.
    */
    /* Never reached. */
    return 0;
}

編譯方法見:https://blog.csdn.net/Rong_Toa/article/details/80188671

$ gcc demo_opengl-2.c `pkg-config --libs --cflags sdl` -lGL -lglut -lGLU -lXmu -Bstatic -Bdyanmic

結果:

3 輸入處理


3.1控制桿處理


3.1.1初始化


在一個SDL 程序中使用一個控制桿的第一步是初始化SDL 的控制桿子系統。傳入SDL_INIT_JOYSTICK 標誌到SDL_Init 函數可以完成這一步。這個控制桿標記通常與其他標誌關聯(像video 標誌)因爲這個控制桿經常用於控制某些東西。


例子3——1 初始化SDL 支持控制桿

#include "SDL.h" /* All SDL App's need this */
#include <stdio.h>

int main() 
{
    printf("Initializing SDL.\n");
    /* Initialize defaults, Video and SDL_INIT_JOYSTICK */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK)==-1)) 
	{
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }
    printf("SDL initialized.\n");
    printf("Quiting SDL.\n");
    /* Shutdown all subsystems */
    SDL_Quit();
    printf("Quiting....\n");
    exit(0);
}

這個將嘗試啓動SDL 的視頻和控制桿子系統。


3.1.2詢問


如果我們通過詢問測試,我們能安全的假設SDL 庫已經被初始化且控制桿子系統開始工作。我們現在能調用一些視頻和(或)音頻功能在我們需要控制桿之前。最後我們必須確認有一個真實的控制桿在工作。即使你知道一個控制桿將在在系統上,它也希望檢查因爲它能幫助識別控制桿被拔去的時候。這個用於檢查控制桿的功能是SDL_NumJoysticks。
這個功能簡單的返回控制桿啓動數目。如果它至少一個。接下來一步是決定哪個控制桿用戶想使用。如果這個控制桿活動個數只有一個然後它會安全的假設這個控制桿是用戶想使用的。SDL 有一個功能來獲取控制桿由系統分配的名字的功能SDL_JoystickName。這個控制桿被劃分爲從0 開始作爲第一個控制桿和最後一個控制桿,這個數目被SDL_NumJoysticks - 1 返回。示範一個所有活動的控制桿清單被打印到stdout。


例子3——2 詢問活動控制桿個數

#include "SDL.h" /* All SDL App's need this */
#include <stdio.h>

int main() 
{
    printf("Initializing SDL.\n");
    /* Initialize defaults, Video and SDL_INIT_JOYSTICK */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK)==-1)) 
	{
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }
    printf("SDL initialized.\n");
    
    printf("%i joysticks were found.\n\n", SDL_NumJoysticks() );
    printf("The names of the joysticks are:\n");
    
    int i;
    for( i=0; i < SDL_NumJoysticks(); i++ )
    {
        printf(" %s\n", SDL_JoystickName(i));
    }
    
    printf("Quiting SDL.\n");
    /* Shutdown all subsystems */
    SDL_Quit();
    printf("Quiting....\n");
    exit(0);
}
$ gcc demo-init-stick.c `pkg-config --libs --cflags sdl`
$ ./a.out 
Initializing SDL.
SDL initialized.
1 joysticks were found.

The names of the joysticks are:
 VirtualPS/2 VMware VMMouse
Quiting SDL.
Quiting....

3.1.3打開一個控制桿接受控制桿事件


SDL 的事件驅動可以打開一個控制桿工作。控制桿能引發4 種類型的事件
SDL_JoyAxisEvent 事件出發當一個座標改變
SDL_JoyBallEvent 事件出現當一個控制桿跟蹤球位置改變
SDL_JoyHatEvent 事件出現當一個帽子的位置改變
SDL_JoyButtonEvent 事件出現當一個按鈕按下或者釋放
事件從所有打開的控制桿接受。爲了接受控制桿事件首先做的事情是調用SDL_JoystickEventState 加上SDL_ENABLE 功能。例如我們我們只對系統中第一個控制桿事件感興趣,不管它是啥。從它接受事件我們要做這個:


例3——3 打開一個控制桿

SDL_Joystick *joystick;
SDL_JoystickEventState(SDL_ENABLE);
joystick = SDL_JoystickOpen(0);

如果我們想接受其他控制桿事件我們需要打開他們而調用SDL_JoystickOpen 就像我們打開控制桿0 一樣,除非我們要存儲SDL_Joystick結構體(他們返回一個不同類型的指針)我們只需要控制桿指針當我們詢問這個控制桿或者當我們關閉這個控制桿。
這之上所有代碼我們只是用於初始化控制桿爲了實時讀取值。所有我們現在需要的是一個事件循環,所有SDL 程序應該有任何方法來接受系統退出事件。我們現在必須添加代碼來檢查事件循環中至少一些以下注意到的事件。讓我們假設我們事件循環像這樣:

SDL_Event event;
/* Other initializtion code goes here */
/* Start main game loop here */
while(SDL_PollEvent(&event))
{
    switch(event.type)
    {
        case SDL_KEYDOWN:
            /* handle keyboard stuff here */
            break;
        case SDL_QUIT:
            /* Set whatever flags are necessary to */
            /* end the main game loop here */
            break;
    }
}
/* End loop here */

處理控制桿事件我們只不過爲他們添加case,首先我們將添加座標處理代碼,座標檢查能獲取棘手的kinda 因爲許多接受的控制桿事件是垃圾。控制桿座標有一個微小改變趨勢只是在它們設計的軌跡路徑之間。你必須設置一個門檻來補償改變和忽略事件如果它們沒有超出門檻。10%是一個通常的很好的門檻值。這個聽起來比它本身更復雜。這裏是一個座標事件處理


例子3——4 控制桿座標事件

case SDL_JOYAXISMOTION: /* Handle Joystick Motion */
    if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) )
    {
    /* code goes here */
    }
    break;

其他座標事件特技例如上——下、左——右動作是兩個不同不同的軸設置。最重要的座標是座標0(左——右)和座標1(上——下)。在代碼中處理它們我們要做以下:


例子3——5 更多控制桿座標事件

case SDL_JOYAXISMOTION: /* Handle Joystick Motion */
    if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) )
    {
        if( event.jaxis.axis == 0)
        {
            /* Left-right movement code goes here */
        }
        if( event.jaxis.axis == 1)
        {
            /* Up-Down movement code goes here */
        }
    }
    break;

理想的代碼應該使用event.jaxis.value 來縮放某些東西。例如讓我們假設你正使用控制桿來控制太空船的動作。如果用戶正使用一個類似的控制桿且他們推動這個杆移動一點,飛船會移動一點。設計你的代碼更好的用於這個情況因爲它是爲使用戶類似的控制更好和同樣的用戶數字控制經驗。
如果你的控制桿有任何另外的座標然後人啊們也許被用於其他杆或者閥門控制且這些座標返回值爲event.jaxis.axis 值。
按鈕處理是簡單對比座標檢查。


例子3——6 控制桿按鈕事件

case SDL_JOYBUTTONDOWN: /* Handle Joystick Button Presses */
    if ( event.jbutton.button == 0 )
    {
        /* code goes here */
    }
    break;

按鈕檢查比座標檢查簡單因爲一個按鈕只有按下或者沒有按下。這個SDL_JOYBUTTONDOWN 事件被觸發當一個按鈕被按下且這個SDL_JOYBUTTONUP 事件被觸發當一個按鈕被釋放。我們必須知道啥子按鈕被按下, 讀取event.jbutton.button 可以完成這個事件。
最後當我們完成使用我們的控制桿我們應該關閉他們調用SDL_JoystickClose。關閉我們開啓控制桿0 我們應該在程序的末尾做這件事:

SDL_JoystickClose(joystick);

整體上:

#include "SDL.h" /* All SDL App's need this */
#include <stdio.h>

int main() 
{
    printf("Initializing SDL.\n");
    /* Initialize defaults, Video and SDL_INIT_JOYSTICK */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK)==-1)) 
	{
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }
    printf("SDL initialized.\n");
    
    printf("%i joysticks were found.\n\n", SDL_NumJoysticks() );
    printf("The names of the joysticks are:\n");
    
    int i;
    for( i=0; i < SDL_NumJoysticks(); i++ )
    {
        printf(" %s\n", SDL_JoystickName(i));
    }
    
    SDL_Joystick *joystick;
    SDL_JoystickEventState(SDL_ENABLE);
    joystick = SDL_JoystickOpen(0);
    
    SDL_Event event;
    /* Other initializtion code goes here */
    /* Start main game loop here */
    while(SDL_PollEvent(&event))
    {
        switch(event.type)
        {
            case SDL_KEYDOWN:
                /* handle keyboard stuff here */
                break;
            case SDL_QUIT:
                /* Set whatever flags are necessary to */
                /* end the main game loop here */
                break;
            case SDL_JOYAXISMOTION: /* Handle Joystick Motion */
                if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) )
                {
                    if( event.jaxis.axis == 0)
                    {
                        /* Left-right movement code goes here */
                    }
                    if( event.jaxis.axis == 1)
                    {
                        /* Up-Down movement code goes here */
                    }
                }
                break;
            case SDL_JOYBUTTONDOWN: /* Handle Joystick Button Presses */
                if ( event.jbutton.button == 0 )
                {
                    /* code goes here */
                }
                break;
        }
    }
    /* End loop here */
    SDL_JoystickClose(joystick);
    
    
    printf("Quiting SDL.\n");
    /* Shutdown all subsystems */
    SDL_Quit();
    printf("Quiting....\n");
    exit(0);
}

3.1.4優化的控制桿功能


關心好控制那麼你能使用在世界上的每種控制桿,但是有一小部分設備SDL不能支持。Joyball 是我們下一個清單目標,它們與我們用的軸有很多的不同。Joyball 存儲相對改變不像軸存儲絕對改變。一個軌跡球事件即包含了x 軸的改變又包含了y 的改變。下面是例子:


例子3——7 控制桿球事件

case SDL_JOYBALLMOTION: /* Handle Joyball Motion */
    if( event.jball.ball == 0 )
    {
        /* ball handling */
    }
    break;

下面的檢查第一個joyball 在控制桿上。這個位置的改變將被存儲到event.jball.xrel 和event.jball.yrel。最後我們有這個hat 事件。Hats 報告只有它們直接壓入的。我們檢查hat's位置使用bitmasks:

/*
SDL_HAT_CENTERED
SDL_HAT_UP
SDL_HAT_RIGHT
SDL_HAT_DOWN
SDL_HAT_LEFT
*/

也有一些預定義聯合體如下:

/*
SDL_HAT_RIGHTUP
SDL_HAT_RIGHTDOWN
SDL_HAT_LEFTUP
SDL_HAT_LEFTDOWN
*/

Hat 的例子如下:


例子3——8 控制桿hat 事件:

case SDL_JOYHATMOTION: /* Handle Hat Motion */
    if ( event.jhat.hat | SDL_HAT_UP )
    {
        /* Do up stuff here */
    }
    if ( event.jhat.hat | SDL_HAT_LEFT )
    {
        /* Do left stuff here */
    }
    if ( event.jhat.hat | SDL_HAT_RIGHTDOWN )
    {
        /* Do right and down together stuff here */
    }
    break;

另外詢問控制桿數量和他們的名字有另外的功能來詢問連接的控制桿能力:
SDL_JoystickNumAxes 返回控制桿的軸數
SDL_JoystickNumButtons 返回控制桿的按鈕數量
SDL_JoystickNumBalls 返回控制桿球的個數
SDL_JoystickNumHats 返回控制桿hats 的個數
使用這些功能我們必須傳入控制桿結構體在我們打開控制桿獲得的。例如:


例子3——9 詢問控制桿特性

/*這個代碼將會得到第一個控制桿的按鈕個數。*/
int number_of_buttons;
SDL_Joystick *joystick;
joystick = SDL_JoystickOpen(0);
number_of_buttons = SDL_JoystickNumButtons(joystick);

整體:

#include "SDL.h" /* All SDL App's need this */
#include <stdio.h>

int main() 
{
    printf("Initializing SDL.\n");
    /* Initialize defaults, Video and SDL_INIT_JOYSTICK */
    if((SDL_Init(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK)==-1)) 
	{
        printf("Could not initialize SDL: %s.\n", SDL_GetError());
        exit(-1);
    }
    printf("SDL initialized.\n");
    
    printf("%i joysticks were found.\n\n", SDL_NumJoysticks() );
    printf("The names of the joysticks are:\n");
    
    int i;
    for( i=0; i < SDL_NumJoysticks(); i++ )
    {
        printf(" %s\n", SDL_JoystickName(i));
    }
    
    SDL_Joystick *joystick;
    SDL_JoystickEventState(SDL_ENABLE);
    joystick = SDL_JoystickOpen(0);
    
    SDL_Event event;
    /* Other initializtion code goes here */
    /* Start main game loop here */
    while(SDL_PollEvent(&event))
    {
        switch(event.type)
        {
            case SDL_KEYDOWN:
                /* handle keyboard stuff here */
                break;
            case SDL_QUIT:
                /* Set whatever flags are necessary to */
                /* end the main game loop here */
                break;
            case SDL_JOYAXISMOTION: /* Handle Joystick Motion */
                if ( ( event.jaxis.value < -3200 ) || (event.jaxis.value > 3200 ) )
                {
                    if( event.jaxis.axis == 0)
                    {
                        /* Left-right movement code goes here */
                    }
                    if( event.jaxis.axis == 1)
                    {
                        /* Up-Down movement code goes here */
                    }
                }
                break;
            case SDL_JOYBUTTONDOWN: /* Handle Joystick Button Presses */
                if ( event.jbutton.button == 0 )
                {
                    /* code goes here */
                }
                break;
            case SDL_JOYBALLMOTION: /* Handle Joyball Motion */
                if( event.jball.ball == 0 )
                {
                    /* ball handling */
                }
                break;
            /*
            SDL_HAT_CENTERED
            SDL_HAT_UP
            SDL_HAT_RIGHT
            SDL_HAT_DOWN
            SDL_HAT_LEFT
            */
            /*
            SDL_HAT_RIGHTUP
            SDL_HAT_RIGHTDOWN
            SDL_HAT_LEFTUP
            SDL_HAT_LEFTDOWN
            */
            case SDL_JOYHATMOTION: /* Handle Hat Motion */
                if ( event.jhat.hat | SDL_HAT_UP )
                {
                    /* Do up stuff here */
                }
                if ( event.jhat.hat | SDL_HAT_LEFT )
                {
                    /* Do left stuff here */
                }
                if ( event.jhat.hat | SDL_HAT_RIGHTDOWN )
                {
                    /* Do right and down together stuff here */
                }
                break;
        }
    }
    
    /*這個代碼將會得到第一個控制桿的按鈕個數。*/
    int number_of_buttons = SDL_JoystickNumButtons(joystick);
    
    /* End loop here */
    SDL_JoystickClose(joystick);
    
    
    printf("Quiting SDL.\n");
    /* Shutdown all subsystems */
    SDL_Quit();
    printf("Quiting....\n");
    exit(0);
}

3.2處理鍵盤


3.2.1鍵盤相關結構體


你如果熟悉數據類型在鍵盤接受時將會更加容易明白這個指導,所以我將首先解釋他們。


3.2.2SDLKey


SDLKey 是一個枚舉類型定義在SDL/include/SDL_keysym.h,具體見表(這個表自己看看英文文檔去吧,實在太多)。每一個SDLKey 記號描述一個鍵,SDLK_a 與鍵盤上的a 按鍵是一致的,SDLK_SPACE 與鍵盤上的空格鍵是一致的,等等。
3.2.3SDLMod
SDLMod 是一個枚舉類型, 與SDLKey 類似, 但是它枚舉鍵盤的控制鍵(Control,Alt,Shift)。這個完整的控制器記號見表(還是看英文文檔) SDLMod 值能聯合起來描述一些控制。

3.2.4SDL_keysym

typedef struct{
    Uint8 scancode;
    SDLKey sym;
    SDLMod mod;
    Uint16 unicode;
} SDL_keysym;

這個SDL_keysym 結構體描述一個按鍵按下或者一個按鍵釋放。其中scancode 參數是硬件描述,它應該被忽略除非你知道你在做什麼。這個sym 參數是被按下或者釋放的SDLKey 的鍵盤值。這個mod 參數描述鍵盤控制器在按下或者釋放發生狀態。所以一個KMOD_NUM|KMOD_CAPS|KMOD_LSHIFT 的意思是Numlock,Capslock 和左邊shift 按鍵全部按下(或者在鎖住按鍵時啓動)。最後,這個unicode 參數存儲鍵盤的16 位的unicode 碼。
注意:應該注意和明白這個參數只有當SDL_keysym 正在描述一個按鍵按下有效時候,不是按鍵釋放的時候。Unicode 值只有在按鍵按下才有意義因爲這個unicode 值描述一個國際符號且只有按鍵按下才產生符號。要查詢unicode 的信息要去它的網站去找。
注意:unicode 翻譯必須使用SDL_EnableUNICODE 起作用。

3.2.5SDL_KeyboardEvent

typedef struct{
    Uint8 type;
    Uint8 state;
    SDL_keysym keysym;
} SDL_KeyboardEvent;

這個SDL_KeyboardEvent 描述一個鍵盤事件(顯然的)。這個SDL_Event 聯合體的key 參數成員是一個SDL_KeyboardEvent 結構體。這個type 參數描述一個事件是鍵盤釋放(SDL_KEYUP)或者是一個鍵盤按下(SDL_KEYDOWN)事件。這個state 參數是非常的多餘,它報告與type 同樣的信息但是用不同的值(SDL_RLEASED 和SDL_PRESSED)。這個keysym 包含鍵盤按下或者釋放信息。

3.2.6讀取鍵盤事件


從事件隊列中讀取鍵盤事件是非常簡單的(這個事件隊列和它的使用在表中有描述)。我們讀取事件使用SDL_PollEvent 在一個while() 循環且檢查SDL_KEYUP 和SDL_KEYDOWN 事件使用一個switch 結構,像這樣例子3——10 讀取鍵盤事件

/* Poll for events. SDL_PollEvent() returns 0 when there are no */
/* more events on the event queue, our while loop will exit when */
/* that occurs. */
while( SDL_PollEvent( &event ) ){
    /* We are only worried about SDL_KEYDOWN and SDL_KEYUP events */
    switch( event.type ){
        case SDL_KEYDOWN:
            printf( "Key press detected\n" );
            break;
        case SDL_KEYUP:
            printf( "Key release detected\n" );
            break;
        default:
            break;
    }
}

3.2.7一個更加詳細的查看


在我們能讀取SDL 事件之前, SDL_Init 必須被開啓且視頻模式必須被設置使用SDL_SetVideoMode 。但是有兩個其他功能我們必須使用來獲得所有響應信息, 使用SDL_GetKeyName。
注意:unicode 編碼值< 0x80 會直接翻譯爲一個ASCII 符號編碼值。這個在下面的例子中使用。


例子3——11 說明鍵盤事件信息

#include "SDL.h"

/* Function Prototypes */
void PrintKeyInfo( SDL_KeyboardEvent *key );
void PrintModifiers( SDLMod mod );

/* main */
int main( int argc, char *argv[] ){
    SDL_Event event;
    int quit = 0;
    /* Initialise SDL */
    if( SDL_Init( SDL_INIT_VIDEO ) ){
    fprintf( stderr, "Could not initialise SDL: %s\n", SDL_GetError() );
    exit( -1 );
    }
    /* Set a video mode */
    if( !SDL_SetVideoMode( 320, 200, 0, 0 ) ){
    fprintf( stderr, "Could not set video mode: %s\n", SDL_GetError() );
    SDL_Quit();
    exit( -1 );
    }
    /* Enable Unicode translation */
    SDL_EnableUNICODE( 1 );
    /* Loop until an SDL_QUIT event is found */
    while( !quit ){
        /* Poll for events */
        while( SDL_PollEvent( &event ) ){
            switch( event.type ){
                /* Keyboard event */
                /* Pass the event data onto PrintKeyInfo() */
                case SDL_KEYDOWN:
                case SDL_KEYUP:
                    PrintKeyInfo( &event.key );
                    break;
                /* SDL_QUIT event (window close) */
                case SDL_QUIT:
                    quit = 1;
                    break;
                default:
                    break;
            }
        }
    }
    /* Clean up */
    SDL_Quit();
    exit( 0 );
}
/* Print all information about a key event */
void PrintKeyInfo( SDL_KeyboardEvent *key ){
    /* Is it a release or a press? */
    if( key->type == SDL_KEYUP )
        printf( "Release:- " );
    else
        printf( "Press:- " );
    /* Print the hardware scancode first */
    printf( "Scancode: 0x%02X", key->keysym.scancode );
    /* Print the name of the key */
    printf( ", Name: %s", SDL_GetKeyName( key->keysym.sym ) );
    /* We want to print the unicode info, but we need to make */
    /* sure its a press event first (remember, release events */
    /* don't have unicode info */
    if( key->type == SDL_KEYDOWN ){
        /* If the Unicode value is less than 0x80 then the */
        /* unicode value can be used to get a printable */
        /* representation of the key, using (char)unicode. */
        printf(", Unicode: " );
        if( key->keysym.unicode < 0x80 && key->keysym.unicode > 0 ){
            printf( "%c (0x%04X)", (char)key->keysym.unicode,
            key->keysym.unicode );
        }
        else{
            printf( "? (0x%04X)", key->keysym.unicode );
        }
    }
    printf( "\n" );
    /* Print modifier info */
    PrintModifiers( key->keysym.mod );
}


/* Print modifier info */
void PrintModifiers( SDLMod mod )
{
    printf( "Modifers: " );
    /* If there are none then say so and return */
    if( mod == KMOD_NONE ){
        printf( "None\n" );
        return;
    }
    /* Check for the presence of each SDLMod value */
    /* This looks messy, but there really isn't */
    /* a clearer way. */
    if( mod & KMOD_NUM ) printf( "NUMLOCK " );
    if( mod & KMOD_CAPS ) printf( "CAPSLOCK " );
    if( mod & KMOD_LCTRL ) printf( "LCTRL " );
    if( mod & KMOD_RCTRL ) printf( "RCTRL " );
    if( mod & KMOD_RSHIFT ) printf( "RSHIFT " );
    if( mod & KMOD_LSHIFT ) printf( "LSHIFT " );
    if( mod & KMOD_RALT ) printf( "RALT " );
    if( mod & KMOD_LALT ) printf( "LALT " );
    if( mod & KMOD_CTRL ) printf( "CTRL " );
    if( mod & KMOD_SHIFT ) printf( "SHIFT " );
    if( mod & KMOD_ALT ) printf( "ALT " );
    printf( "\n" );
}

3.2.8遊戲模式輸入


我發現人們在遊戲中和其他使用鍵盤事件和其他交互應用總是一直不明白一個fundemental 點。
鍵盤事件只有在一個按鍵從沒有按下到按下的狀態發生時候,且取代多功能。
想象你有一個圖像想從外面移動過來使用光標按鍵——當你按下左箭頭按鍵你想讓它滑動到左邊,當你按下這個向下按鍵你想向下滑動。測試下面的代碼,它是許多人會犯的錯誤。

/* Alien screen coordinates */
int alien_x=0, alien_y=0;
/* Initialise SDL and video modes and all that */
/* Main game loop */
/* Check for events */
while( SDL_PollEvent( &event ) ){
    switch( event.type ){
        /* Look for a keypress */
        case SDL_KEYDOWN:
            /* Check the SDLKey values and move change the coords */
            switch( event.key.keysym.sym ){
        case SDLK_LEFT:
            alien_x -= 1;
            break;
        case SDLK_RIGHT:
            alien_x += 1;
            break;
        case SDLK_UP:
            alien_y -= 1;
            break;
        case SDLK_DOWN:
            alien_y += 1;
            break;
        default:
            break;
        }
    }
}

首先你也許認爲這是一個完美的響應代碼,但是這不是。像我說的鍵盤事件只有當一個鍵狀態改變時發生,所以用戶將必須按下且釋放這個左箭頭按鍵100 次來移動像右邊100像素。
避免這個問題我們必須不使用這個事件來改變外部的位置,我們使用事件來設置用於一個區分選擇代碼移動到外部標記。就像這樣:


例子3——12 適當的遊戲移動

/* Alien screen coordinates */
int alien_x=0, alien_y=0;
int alien_xvel=0, alien_yvel=0;
/* Initialise SDL and video modes and all that */
/* Main game loop */
/* Check for events */
while( SDL_PollEvent( &event ) ){
    switch( event.type ){
        /* Look for a keypress */
        case SDL_KEYDOWN:
            /* Check the SDLKey values and move change the coords */
            switch( event.key.keysym.sym ){
                case SDLK_LEFT:
                    alien_xvel = -1;
                    break;
                case SDLK_RIGHT:
                    alien_xvel = 1;
                    break;
                case SDLK_UP:
                    alien_yvel = -1;
                    break;
                case SDLK_DOWN:
                    alien_yvel = 1;
                    break;
                default:
                    break;
            }
            break;
        /* We must also use the SDL_KEYUP events to zero the x */
        /* and y velocity variables. But we must also be */
        /* careful not to zero the velocities when we shouldn't*/
        case SDL_KEYUP:
            switch( event.key.keysym.sym ){
                case SDLK_LEFT:
                    /* We check to make sure the alien is moving */
                    /* to the left. If it is then we zero the */
                    /* velocity. If the alien is moving to the */
                    /* right then the right key is still press */
                    /* so we don't tocuh the velocity */
                    if( alien_xvel < 0 )
                    alien_xvel = 0;
                    break;
                case SDLK_RIGHT:
                    if( alien_xvel > 0 )
                    alien_xvel = 0;
                    break;
                case SDLK_UP:
                    if( alien_yvel < 0 )
                    alien_yvel = 0;
                    break;
                case SDLK_DOWN:
                    if( alien_yvel > 0 )
                    alien_yvel = 0;
                    break;
                default:
                    break;
            }
            break;
        default:
        break;
    }
}
/* Update the alien position */
alien_x += alien_xvel;
alien_y += alien_yvel;

可以被看到的,我們使用兩個額外的差異,alien_xvel 和alien_vvel,描述了船的移動,這些差異我們下載當我們發現鍵盤按下和釋放。

參考/複製粘貼自:《SDL中文資料.pdf》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章