SDL Guide 中文译版

SDL即Simple DirectMedia Layer,类似DirectX,是完整的游戏、多媒体开发包,但不同的是它跨越几乎所有的平台,有各种语言的接口,多种语言的文档,而这一切都是广大志愿者完成的。目前扩展部分还没有正式的文档,以下为核心部分文档的向导部分。

序言

关于SDL

SDL为方便制作能跨跃Linux、BSD、MacOS、Win32和BeOS平台,使用本地高性能媒体接口,并且让您可以只需使用一份源代码级API而设计。SDL是相当低层的API,但使用它可以让你以极大的灵活性写出完全跨平台的程序。

 SDL:http://www.libsdl.org/

关于SDL文档

SDLdoc项目即要重新编写SDL文档并同步更新。项目组有使用SDL的志愿者组成。

最新版本可在http://sdldoc.sourceforge.net下载

谢启

Sam Lantinga, slouken@libsdl.org

Martin Donlon, akawaka@skynet.ie

Mattias Engdeg?rd

Julian Peterson

Ken Jordan

Maxim Sobolev

Wesley Poole

Michael Vance

Andreas Umbach

Andreas Hofmeister


第一章 基础

初始化

SDL由八个子系统组成——音频、CDROM、事件处理、文件I/O、游戏杆、线程、记时器和视频。使用前必须调用SDL_Init或SDL_InitSubSystem初始化。SDL_Init必须早于其他所有SDL调用,它将自动初始化事件处理、文件I/O和线程子系统,并根据参数选择启动其他子系统。例如,初始化缺省和视频子系统:

SDL_Init(SDL_INIT_VIDEO);

初始化缺省、视频和记时器子系统:

SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);

SDL_Init对应SDL_Quit(和SDL_QuitSubSystem)。SDL_Quit关闭所有子系统,必须在程序关闭前调用。

除此之外,我们还必须进行错误处理。很多SDL函数返回一个值指示成功与否。例如SDL_Init失败时返回-1。每当SDL出错时,错误信息被保存,并可用SDL_GetError取得。

例1-1 初始化SDL

#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);
}

第二章 图像和视频

初始化SDL Video显示

视频是最常用的部分,也是SDL最完整的子系统。下面的初始化过程是每个SDL程序都要做的,即使可能有些不同。

例2-1 初始化视频

    SDL_Surface *screen;

    /* Initialize the SDL library */
    if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
        fprintf(stderr,
                "Couldn't initialize SDL: %s/n", SDL_GetError());
        exit(1);
    }

    /* 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);
    }

初始化最佳视频模式

如果你希望某种色深(颜色数)但如果用户的显示器不支持也可以接受其他色深,使用加SDL_ANYFORMAT参数的SDL_SetVideoMode。您还可以用SDL_VideoModeOK来找到与请求模式最接近的模式。

例2-2 初始化最佳视频模式

    /* 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);

读取并显示BMP文件

当SDL已经初始化,视频模式已经选择,下面的函数将读取并显示指定的BMP文件。

例2-3 读取并显示BMP文件

void display_bmp(char *file_name)
{
    SDL_Surface *image;

    /* 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;
    }

    /*
     * 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);
}

直接在显示上绘图

下面两个函数实现在图像平面的像素读写。它们被细心设计成可以用于所有色深。记住在使用前要先锁定图像平面,之后要解锁。

在像素值和其红、绿、蓝值间转换,使用SDL_GetRGB()和SDL_MapRGB()。

例2-4 getpixel()

/*
 * 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 */
    }
}

例2-5 putpixel()

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

    switch(bpp) {
    case 1:
        *p = pixel;
        break;

    case 2:
        *(Uint16 *)p = pixel;
        break;

    case 3:
        if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;
        } else {
            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;
        }
        break;

    case 4:
        *(Uint32 *)p = pixel;
        break;
    }
}

例2-6 使用上面的putpixel()在屏幕中心画一个黄点

    /* 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);

    return;

并用SDL和OpenGL

SDL可以在多种平台(Linux/X11, Win32, BeOS, MacOS Classic/Toolbox, MacOS X, FreeBSD/X11 and Solaris/X11)上创建和使用OpenGL上下文。这允许你在OpenGL程序中使用SDL的音频、事件、线程和记时器,而这些通常是GLUT的任务。

和普通的初始化类似,但有三点不同:必须传SDL_OPENGL参数给SDL_SetVideoMode;必须使用SDL_GL_SetAttribute指定一些GL属性(深度缓冲区位宽,帧缓冲位宽等);如果您想使用双缓冲,必须作为GL属性指定

例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 );
    }

OpenGL绘图

除了初始化,在SDL程序中使用OpenGL和其他情况基本相同,是同样函数和数据类型。但是如果您使用双缓冲,则必须用SDL_GL_SwapBuffers()来交换前后缓冲,而不是glxSwapBuffers()(GLX)或SwapBuffers()(Windows)。

例2-8 SDL和OpenGL

/*
 * SDL OpenGL Tutorial.
 * (c) Michael Vance, 2000
 * briareos@lokigames.com
 *
 * 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;
}

第三章上 游戏杆输入处理

初始化

使用游戏杆的第一步是初始化游戏杆子系统。即在SDL_Init时使用参数SDL_INIT_JOYSTICK。

例3-1 初始化SDL并带游戏杆支持

    if ( ! SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) )
    {
        fprintf(stderr, "Couldn't initialize SDL: %s/n", SDL_GetError());
        exit(1);
    }

此例启动SDL并带视频和游戏杆支持。

查询

至此,我们可以假定初始化以完成。但我们还需指导有没有、有几个游戏杆。即使您指导有一个游戏杆可用,也最好经常检查,因为这样可以帮助检测游戏杆被拔掉的情况。

检查游戏杆的函数是SDL_NumJoysticks()。此函数简单的返回系统中游戏杆的数量。下一步是决定用户想用哪一个。当然,如果只有一个就不用决定了。SDL_JoystickName取得系统赋给游戏杆的名字。游戏杆用序号指定,第一个为0,最后一个为SDL_NumJoysticks - 1。

例3-2 打印所有游戏杆的名字

    printf("%i joysticks were found./n/n", SDL_NumJoysticks() );
    printf("The names of the joysticks are:/n");

    for( i=0; i < SDL_NumJoysticks(); i++ )
    {
        printf("    %s/n", SDL_JoystickName(i));
    }

启动游戏杆取得事件

SDL使用事件架构,游戏杆可以触发四种事件。

  • SDL_JoyAxisEvent 轴改变
  • SDL_JoyBallEvent 轨迹球坐标改变
  • SDL_JoyHatEvent hat(sorry,在下不解何为hat)方向改变
  • SDL_JoyButtonEvent 按钮按下或释放

所有启动的游戏杆都会触发事件。为了收到事件,首先要用SDL_ENABLE调用SDL_JoystickEventState,以允许事件。其次要启动指定的游戏杆,用SDL_JoystickOpen()。

例3-3 启动第一个游戏杆

    SDL_Joystick *joystick;

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

游戏杆对象的指针只在查询和关闭游戏杆时有用。

现在为了响应事件,我们需要一个消息循环。所有的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。轴检测得有些技巧,因为大部分事件是垃圾。摇杆动一点就会有事件。所以必须设定一个阈值,并且忽略未达到阈值的事件。一般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);

高级游戏杆函数

轨迹球消息包含X和Y方向的改变量。

例3-7 轨迹球事件

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

此例检测第一个轨迹球。坐标改变量在event.jball.xrel和event.jball.yrel中。

最后是hat事件。hat只报告方向。我们通过位掩码检测:

 

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

例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 hat数量

只需将启动游戏杆时得到的指针传给这些函数即可。

例3-9 查询游戏杆特性

int number_of_buttons;
    SDL_Joystick *joystick;

    joystick = SDL_JoystickOpen(0);
    number_of_buttons = SDL_JoystickNumButtons(joystick);

第三章下 键盘输入

键盘相关数据结构

  • SDLKey 枚举类型,每一个符号代表一个键,如SDLK_a、SDLK_SPACE,在SDL_keysym.h中定义。
  • SDLMod 枚举类型,类似SDLKey,但用于修饰键,如Control、Alt、Shift,可以组合。
  • SDL_keysym
    typedef struct{
      Uint8 scancode;
      SDLKey sym;
      SDLMod mod;
      Uint16 unicode;
    } SDL_keysym;
    

    用于按键和放开消息。scancode是硬件相关键码。除非你有特殊用途,否则忽略。sym指示键,mod为修饰键集合,例如KMOD_NUM | KMOD_CAPS | KMOD_LSHIFT 为Numlock、Capslock和左Shift组合。最后unicode为对应字符。注意,仅当是按键消息时unicode才有效,放开消息时无效。Unicode变换必须用SDL_EnableUNICODE()打开。

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

    描述一个键盘消息。type指示按键(SDL_KEYDOWN)或放开(SDL_KEYUP)。state是冗余的,和含义type相同只是符号不同(SDL_PRESSED,SDL_RELEASED)。

读取键盘消息

使用消息循环用SDL_PollEvent()从消息队列里读取,并用switch-case检测SDL_KEYUP和SDL_KEYDOWN。下面是个基本的例子。

例3-10 读键盘消息

  SDL_Event event;
  .
  .
  /* 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;
    }
  }

更详细的内容

我们已经知道要用SDL_Init和SDL_SetVideoMode初始化,但我们还得用另外两个函数取得必要信息。要调用SDL_EnableUNICODE(1)以允许unicode变换,还要用SDL_GetKeyName将SDLKey转换成可打印字符。注意:小于0x80的unicode字符直接映射到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" );
    }

游戏式键盘输入

键盘消息仅在键的状态在按下和放开间变化时才触发。

设想你用光标键控制飞船运动以改变眼前看到的太空景象,当你按左键并希望镜头向左转时。看看下面的代码,并注意它为什么是错的。

    /* 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次键盘消息,才能向左转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_yvel来表示飞船的运动,并在响应键盘消息时更新它们。

第四章 样例

注:重复的例子没有列出

最快的图像平面块传送

将图像画到屏幕上有三种方式:1.创建一个图像平面并用SDL_BlitSurface传送到屏幕;2.在系统内存创建视频平面并调用SDL_UpdateRect;3.在显存创建视频平面并调用SDL_LockSurface。最好的方法是混合方式:

#include <stdio.h>
#include <stdlib.h>
#include "SDL.h"
#include "SDL_timer.h"

void ComplainAndExit(void)
{
    fprintf(stderr, "Problem: %s/n", SDL_GetError());
    exit(1);
}

int main(int argc, char *argv[])
{
    SDL_PixelFormat fmt;
    SDL_Surface *screen, *locked;
    SDL_Surface *imagebmp, *image;
    SDL_Rect dstrect;
    int i;
    Uint8 *buffer;

    /* Initialize SDL */
    if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
        ComplainAndExit();
    }
    atexit(SDL_Quit);

    /* Load a BMP image into a surface */
    imagebmp = SDL_LoadBMP("image.bmp");
    if ( imagebmp == NULL ) {
        ComplainAndExit();
    }

    /* Set the video mode (640x480 at native depth) */
    screen = SDL_SetVideoMode(640, 480, 0, SDL_HWSURFACE|SDL_FULLSCREEN);
    if ( screen == NULL ) {
        ComplainAndExit();
    }

    /* Set the video colormap */
    if ( imagebmp->format->palette != NULL ) {
        SDL_SetColors(screen,
                      imagebmp->format->palette->colors, 0,
                      imagebmp->format->palette->ncolors);
    }

    /* Convert the image to the video format (maps colors) */
    image = SDL_DisplayFormat(imagebmp);
    SDL_FreeSurface(imagebmp);
    if ( image == NULL ) {
        ComplainAndExit();
    }

    /* Draw bands of color on the raw surface */
    if ( SDL_MUSTLOCK(screen) ) {
        if ( SDL_LockSurface(screen) < 0 )
            ComplainAndExit();
    }
    buffer=(Uint8 *)screen->pixels;
    for ( i=0; ih; ++i ) {
        memset(buffer,(i*255)/screen->h,
               screen->w*screen->format->BytesPerPixel);
               buffer += screen->pitch;
    }
    if ( SDL_MUSTLOCK(screen) ) {
        SDL_UnlockSurface(screen);
    }

    /* Blit the image to the center of the screen */
    dstrect.x = (screen->w-image->w)/2;
    dstrect.y = (screen->h-image->h)/2;
    dstrect.w = image->w;
    dstrect.h = image->h;
    if ( SDL_BlitSurface(image, NULL, screen, &dstrect) < 0 ) {
        SDL_FreeSurface(image);
        ComplainAndExit();
    }
    SDL_FreeSurface(image);

    /* Update the screen */
    SDL_UpdateRects(screen, 1, &dstrect);

    SDL_Delay(5000);        /* Wait 5 seconds */
    exit(0);
}

过滤和处理事件

#include <stdio.h>
#include <stdlib.h>

#include "SDL.h"

/* This function may run in a separate event thread */
int FilterEvents(const SDL_Event *event) {
    static int boycott = 1;

    /* This quit event signals the closing of the window */
    if ( (event->type == SDL_QUIT) && boycott ) {
        printf("Quit event filtered out -- try again./n");
        boycott = 0;
        return(0);
    }
    if ( event->type == SDL_MOUSEMOTION ) {
        printf("Mouse moved to (%d,%d)/n",
                event->motion.x, event->motion.y);
        return(0);    /* Drop it, we've handled it */
    }
    return(1);
}

int main(int argc, char *argv[])
{
    SDL_Event event;

    /* Initialize the SDL library (starts the event loop) */
    if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
        fprintf(stderr,
                "Couldn't initialize SDL: %s/n", SDL_GetError());
        exit(1);
    }

    /* Clean up on exit, exit on window close and interrupt */
    atexit(SDL_Quit);

    /* Ignore key events */
    SDL_EventState(SDL_KEYDOWN, SDL_IGNORE);
    SDL_EventState(SDL_KEYUP, SDL_IGNORE);

    /* Filter quit and mouse motion events */
    SDL_SetEventFilter(FilterEvents);

    /* The mouse isn't much use unless we have a display for reference */
    if ( SDL_SetVideoMode(640, 480, 8, 0) == NULL ) {
        fprintf(stderr, "Couldn't set 640x480x8 video mode: %s/n",
                        SDL_GetError());
        exit(1);
    }

    /* Loop waiting for ESC+Mouse_Button */
    while ( SDL_WaitEvent(&event) >= 0 ) {
        switch (event.type) {
            case SDL_ACTIVEEVENT: {
                if ( event.active.state & SDL_APPACTIVE ) {
                    if ( event.active.gain ) {
                        printf("App activated/n");
                    } else {
                        printf("App iconified/n");
                    }
                }
            }
            break;

            case SDL_MOUSEBUTTONDOWN: {
                Uint8 *keys;

                keys = SDL_GetKeyState(NULL);
                if ( keys[SDLK_ESCAPE] == SDL_PRESSED ) {
                    printf("Bye bye.../n");
                    exit(0);
                }
                printf("Mouse button pressed/n");
            }
            break;

            case SDL_QUIT: {
                printf("Quit requested, quitting./n");
                exit(0);
            }
            break;
        }
    }
    /* This should never happen */
    printf("SDL_WaitEvent error: %s/n", SDL_GetError());
    exit(1);
}

打开音频设备

    SDL_AudioSpec wanted;
    extern void fill_audio(void *udata, Uint8 *stream, int len);

    /* Set the audio format */
    wanted.freq = 22050;
    wanted.format = AUDIO_S16;
    wanted.channels = 2;    /* 1 = mono, 2 = stereo */
    wanted.samples = 1024;  /* Good low-latency value for callback */
    wanted.callback = fill_audio;
    wanted.userdata = NULL;

    /* Open the audio device, forcing the desired format */
    if ( SDL_OpenAudio(&wanted, NULL) < 0 ) {
        fprintf(stderr, "Couldn't open audio: %s/n", SDL_GetError());
        return(-1);
    }
    return(0);

播放音频

    static Uint8 *audio_chunk;
    static Uint32 audio_len;
    static Uint8 *audio_pos;

    /* The audio function callback takes the following parameters:
       stream:  A pointer to the audio buffer to be filled
       len:     The length (in bytes) of the audio buffer
    */
    void fill_audio(void *udata, Uint8 *stream, int len)
    {
        /* Only play if we have data left */
        if ( audio_len == 0 )
            return;

        /* Mix as much data as possible */
        len = ( len > audio_len ? audio_len : len );
        SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME)
        audio_pos += len;
        audio_len -= len;
    }

    /* Load the audio data ... */

    ;;;;;

    audio_pos = audio_chunk;

    /* Let the callback function play the audio chunk */
    SDL_PauseAudio(0);

    /* Do some processing */

    ;;;;;

    /* Wait for sound to complete */
    while ( audio_len > 0 ) {
        SDL_Delay(100);         /* Sleep 1/10 second */
    }
    SDL_CloseAudio();

列出所有CDROM

    #include "SDL.h"

    /* Initialize SDL first */
    if ( SDL_Init(SDL_INIT_CDROM) < 0 ) {
        fprintf(stderr, "Couldn't initialize SDL: %s/n",SDL_GetError());
        exit(1);
    }
    atexit(SDL_Quit);

    /* Find out how many CD-ROM drives are connected to the system */
    printf("Drives available: %d/n", SDL_CDNumDrives());
    for ( i=0; i<SDL_CDNumDrives(); ++i ) {
        printf("Drive %d:  /"%s/"/n", i, SDL_CDName(i));
    }

打开缺省CDROM驱动器

    SDL_CD *cdrom;
    CDstatus status;
    char *status_str;

    cdrom = SDL_CDOpen(0);
    if ( cdrom == NULL ) {
        fprintf(stderr, "Couldn't open default CD-ROM drive: %s/n",
                        SDL_GetError());
        exit(2);
    }

    status = SDL_CDStatus(cdrom);
    switch (status) {
        case CD_TRAYEMPTY:
            status_str = "tray empty";
            break;
        case CD_STOPPED:
            status_str = "stopped";
            break;
        case CD_PLAYING:
            status_str = "playing";
            break;
        case CD_PAUSED:
            status_str = "paused";
            break;
        case CD_ERROR:
            status_str = "error state";
            break;
    }
    printf("Drive status: %s/n", status_str);
    if ( status >= CD_PLAYING ) {
        int m, s, f;
        FRAMES_TO_MSF(cdrom->cur_frame, &m, &s, &f);
        printf("Currently playing track %d, %d:%2.2d/n",
        cdrom->track[cdrom->cur_track].id, m, s);
    }

列出CD上所有音轨

    SDL_CD *cdrom;          /* Assuming this has already been set.. */
    int i;
    int m, s, f;

    SDL_CDStatus(cdrom);
    printf("Drive tracks: %d/n", cdrom->numtracks);
    for ( i=0; i<cdrom->numtracks; ++i ) {
        FRAMES_TO_MSF(cdrom->track[i].length, &m, &s, &f);
        if ( f > 0 )
            ++s;
        printf("/tTrack (index %d) %d: %d:%2.2d/n", i,
        cdrom->track[i].id, m, s);
    }

播放CD

    SDL_CD *cdrom;          /* Assuming this has already been set.. */

    // Play entire CD:
    if ( CD_INDRIVE(SDL_CDStatus(cdrom)) )
        SDL_CDPlayTracks(cdrom, 0, 0, 0, 0);

        // Play last track:
        if ( CD_INDRIVE(SDL_CDStatus(cdrom)) ) {
            SDL_CDPlayTracks(cdrom, cdrom->numtracks-1, 0, 0, 0);
        }

        // Play first and second track and 10 seconds of third track:
        if ( CD_INDRIVE(SDL_CDStatus(cdrom)) )
            SDL_CDPlayTracks(cdrom, 0, 0, 2, 10);

基于时间的游戏主循环

#define TICK_INTERVAL    30

Uint32 TimeLeft(void)
{
    static Uint32 next_time = 0;
    Uint32 now;

    now = SDL_GetTicks();
    if ( next_time <= now ) {
        next_time = now+TICK_INTERVAL;
        return(0);
    }
    return(next_time-now);
}

/* main game loop

    while ( game_running ) {
        UpdateGameState();
        SDL_Delay(TimeLeft());
    }
时间: 2024-12-21 01:44:51

SDL Guide 中文译版的相关文章

100行代码实现最简单的基于FFMPEG+SDL的视频播放器(SDL1.x)【转】

转自:http://blog.csdn.net/leixiaohua1020/article/details/8652605 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[-] 简介 流程图 simplest_ffmpeg_player标准版代码 simplest_ffmpeg_player_suSU版代码 结果 FFMPEG相关学习资料 补充问题     ===================================================== 最简单的

[总结]FFMPEG视音频编解码零基础学习方法--转

ffmpeg编解码学习   目录(?)[-] ffmpeg程序的使用ffmpegexeffplayexeffprobeexe 1 ffmpegexe 2 ffplayexe 3 ffprobeexe ffmpeg库的使用视频播放器 1 ffmpeg库的配置 2 最简单的视频播放器 3 相关结构体的研究 ffmpeg库的使用音频播放器 1 最简单的音频播放器 ffmpeg库的使用一个真正的播放器ffplay 1 真正的播放器 ffmpeg库的使用编码 1 编码 2 转码 ffmpeg源代码分析 F

FFMPEG视音频编解码零基础学习方法

在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的"大神",有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在"大神"和初学者之间好像有一个不可逾越的鸿沟."大神"们水平高超,探讨着深奥的问题:而初学者们还停留在入门阶段.究竟是什么原因造成的这种"两极分化"呢?最后,我发现了问题的关键:FFMPEG难度比较大,却没有一个循序渐进,由简单到复

在SDL工程中让SDL_ttf渲染汉字

有时候在关于SDL的博文中看到一些评论,说SDL对中文的支持不佳,因为当程序涉及中文时总是输出乱码.   照我个人观点,这里面很多都是误解.下面就根据我在windows下使用SDL的情况,说说我的观点.   SDL作为一个跨平台的库,在字符方面有它独特的地方.那就是,它的运行库支持的字符编码为UTF8,而不是windows中常见的各种本地字符编码.比如中文版windows使用的codepage 936,也有称其为GBK的,实际上是对基于GB2312字符集的EUC-CN编码方式做了一个基于UNIC

什么叫Ajax?(Ajax基础教程)

ajax|基础教程 由于在研究web即时通技术(TRACQ,网眼,网站商务通),所以对Ajax感兴趣起来,最近好像Ajax很火,从搜索引擎的结果可见一斑.baidu找到181,000篇相关文档,google找到了6,790,000篇.我觉得,在没有提出Ajax这个概念以前,肯定有人已经在利用这些技术做事情了,只是在最近才被炒的热闹起来.我先把网上找的资料整理在这里. 1.定义 Ajax最早是由Jesse James Garrett提出来的,原文:Ajax: A New Approach to W

CSS网页实例:适合所有项目通用的reset.css

文章简介:另一个是用于特定项目的"reset",自定义修改的内容,这些内容可以放在layout.css.typography.css之类的文件中,他们共同导入到一个base.css形成一个项目的基础样式.本文就是来介绍如何写一个合适所有项目的通用的reset.css,以及介绍在设置玩reset.css之后需要针对 0,引言每每有新项目,第一步就是应当使用一个reset.css来重置样式.滥用不如不用,直接拿个现成的reset.css过来将导致后期各种离奇bug的发生.所以最好还是自己写

CSS通用框架:打造自己的Reset.css

0,引言每每有新项目,第一步就是应当使用一个reset.css来重置样式.滥用不如不用,直接拿个现成的reset.css过来将导致后期各种离奇bug的发生.所以最好还是自己写一个reset.css,并且要明白每一条reset都是用来做什么的.reset.css本意就是重置样式,我始终建议把.clearfix放入layout.css,而把h1.h2之类的定义放进typography.css. 具体如何规划网站CSS结构,不在文本讨论之列,可以参考Smashing Magazine上的文章,国内有差

程序员必知的前端演进史

细细整理了过去接触过的那些前端技术,发现前端演进是段特别有意思的历史.人们总是在过去就做出未来需要的框架,而现在流行的是过去发明过的.如,响应式设计不得不提到的一个缺点是:它只是将原本在模板层做的事,放到了样式(CSS)层来完成. 复杂度同力一样不会消失,也不会凭空产生,它总是从一个物体转移到另一个物体或一种形式转为另一种形式. 如果六.七年前的移动网络速度和今天一样快,那么直接上的技术就是响应式设计,APP.SPA就不会流行得这么快.尽管我们可以预见未来这些领域会变得更好,但是更需要的是改变现

独家 | 一文读懂集成学习(附学习资源)

集成算法(Ensemble Algorithms)综述 严格意义上来说,这不算是一种机器学习算法,而更像是一种优化手段或者策略,它通常是结合多个简单的弱机器学习算法,去做更可靠的决策.有人把它称为机器学习中的"屠龙刀",非常万能且有效,集成模型是一种能在各种的机器学习任务上提高准确率的强有力技术,集成算法往往是很多数据竞赛关键的一步,能够很好地提升算法的性能.哲学思想为"三个臭皮匠赛过诸葛亮".拿分类问题举个例,直观的理解,就是单个分类器的分类是可能出错,不可靠的,