点X关闭出窗口只是SDL能够处理的事件之一。另一种在游戏中大量使用的输入方式是键盘。在本教程中,我们将根据您所按的键来使不同的图像出现。
//按键表面常量
enum KeyPressSurfaces
{
KEY_PRESS_SURFACE_DEFAULT,
KEY_PRESS_SURFACE_UP,
KEY_PRESS_SURFACE_DOWN,
KEY_PRESS_SURFACE_LEFT,
KEY_PRESS_SURFACE_RIGHT,
KEY_PRESS_SURFACE_TOTAL
};
在源代码的顶部,我们声明了一个不同表面的枚举。枚举是一种做符号常量的简便方法,而不必写const int KEY_PRESS_SURFACE_DEFAULT = 0; const int KEY_PRESS_SURFACE_UP = 1; const int KEY_PRESS_SURFACE_DOWN = 2;
等等。它们默认从0开始计数,你每声明一个枚举就自动加1。这意味着KEY_PRESS_SURFACE_DEFAULT
是0,KEY_PRESS_SURFACE_UP
是1,KEY_PRESS_SURFACE_DOWN
是2,KEY_PRESS_SURFACE_LEFT
是3,KEY_PRESS_SURFACE_RIGHT
是4,KEY_PRESS_SURFACE_TOTAL
是5,可以给它们明确的整数值,但我们在这里不涉及这个问题。在Google上快速搜索一下枚举,应该可以涵盖这些内容。
初级程序员有一个坏习惯,就是使用数字代替符号常量。例如他们会有1个平均的主菜单,2个平均的选项等,这对于小程序来说是很好的。当你要处理数千行代码时(视频游戏通常就是这样),有一行写着 if( option == 1 )
的代码会比使用 if( option == MAIN_MENU )
产生更多的麻烦。
//Starts up SDL and creates window
bool init();
//Loads media
bool loadMedia();
//释放媒体并关闭SDL
void close();
//加载单个图像
SDL_Surface* loadSurface( std::string path );
//我们要渲染的窗口
SDL_Window* gWindow = NULL;
//窗口所包含的表面
SDL_Surface* gScreenSurface = NULL;
//按键对应的图片
SDL_Surface* gKeyPressSurfaces[ KEY_PRESS_SURFACE_TOTAL ];
//当前显示的图像
SDL_Surface* gCurrentSurface = NULL;
除了我们常用的函数原型,我们还有一个新的函数叫loadSurface。有一个普遍的规则,如果你复制/粘贴代码,你就做错了什么。与其每次都复制/粘贴加载代码,不如用一个函数来处理。
对于这个特定的程序来说,重要的是我们有一个指向SDL表面的指针数组,称为gKeyPressSurfaces,以包含我们将使用的所有图像。根据用户按下的键,我们将把 gCurrentSurface (这是将被渲染到屏幕上的图像) 设置为这些表面中的一个。
SDL_Surface* loadSurface( std::string path ){
//在指定路径加载图像
SDL_Surface* loadedSurface = SDL_LoadBMP( path.c_str() );
if( loadedSurface == NULL )
{
printf( "Unable to load image %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
}
return loadedSurface;
}
这里是loadSurface函数,它可以加载图像,并在出现问题时报告错误。它和之前的功能差不多,但是把图像加载和错误报告都包含在一个函数中,这样就很容易添加和调试图像加载。
由于新的C++程序员经常问我这个问题,所以这个函数不会泄漏内存。它确实会分配内存来加载一个新的SDL曲面,并在不释放所分配的内存的情况下返回它,但是分配表面并立即deallocating它有什么意义呢?这个函数所做的是加载表面并返回新加载的表面,这样调用这个函数的人就可以在使用完这个曲面后再重新分配它。在这个程序中,加载的表面在close函数中被deallocated。
bool loadMedia(){
//加载成功标志
bool success = true;
//加载默认表面
gKeyPressSurfaces[ KEY_PRESS_SURFACE_DEFAULT ] = loadSurface( "04_key_presses/press.bmp" );
if( gKeyPressSurfaces[ KEY_PRESS_SURFACE_DEFAULT ] == NULL )
{
printf( "Failed to load default image!\n" );
success = false;
}
//加载up对应的表面
gKeyPressSurfaces[ KEY_PRESS_SURFACE_UP ] = loadSurface( "04_key_presses/up.bmp" );
if( gKeyPressSurfaces[ KEY_PRESS_SURFACE_UP ] == NULL )
{
printf( "Failed to load up image!\n" );
success = false;
}
//加载down对应的表面
gKeyPressSurfaces[ KEY_PRESS_SURFACE_DOWN ] = loadSurface( "04_key_presses/down.bmp" );
if( gKeyPressSurfaces[ KEY_PRESS_SURFACE_DOWN ] == NULL )
{
printf( "Failed to load down image!\n" );
success = false;
}
//加载left对应的表面
gKeyPressSurfaces[ KEY_PRESS_SURFACE_LEFT ] = loadSurface( "04_key_presses/left.bmp" );
if( gKeyPressSurfaces[ KEY_PRESS_SURFACE_LEFT ] == NULL )
{
printf( "Failed to load left image!\n" );
success = false;
}
//加载right对应的表面
gKeyPressSurfaces[ KEY_PRESS_SURFACE_RIGHT ] = loadSurface( "04_key_presses/right.bmp" );
if( gKeyPressSurfaces[ KEY_PRESS_SURFACE_RIGHT ] == NULL )
{
printf( "Failed to load right image!\n" );
success = false;
}
return success;
}
在这里的loadMedia函数中,我们加载了所有要渲染到屏幕上的图片。
//Main loop flag
bool quit = false;
//Event handler
SDL_Event e;
//设置默认的当前表面
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_DEFAULT ];
//While application is running
while( !quit )
{
//Handle events on queue
while( SDL_PollEvent( &e ) != 0 )
{
//用户请求退出
if( e.type == SDL_QUIT )
{
quit = true;
}
//用户按下一个键
else if( e.type == SDL_KEYDOWN )
{
//根据按键选择表面
switch( e.key.keysym.sym )
{
case SDLK_UP:
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_UP ];
break;
case SDLK_DOWN:
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_DOWN ];
break;
case SDLK_LEFT:
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_LEFT ];
break;
case SDLK_RIGHT:
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_RIGHT ];
break;
default:
gCurrentSurface = gKeyPressSurfaces[ KEY_PRESS_SURFACE_DEFAULT ];
break;
}
}
}
这是我们的事件循环。正如你所看到的,我们像之前的教程一样处理关闭窗口,然后我们处理SDL_KEYDOWN事件。当你按下键盘上的一个键时,这个事件就会发生。
在SDL事件[1]里面有一个SDL Keyboard事件[2],它包含了按键事件的信息。在这个事件中,有一个SDL Keysym[3],它包含了被按下的键的信息。这个Keysym包含了SDL Keycode[4],它标识了被按下的键。
正如你所看到的,这段代码的作用是根据被按下的键来设置表面。如果您想知道其他键的键码是什么,请查阅SDL文档。
//Apply the current image
SDL_BlitSurface( gCurrentSurface, NULL, gScreenSurface, NULL );
//Update the surface
SDL_UpdateWindowSurface( gWindow );
在处理完按键并设置好表面后,我们将选定的表面显示到屏幕上。
[1]
SDL事件: https://wiki.libsdl.org/SDL_Event?highlight=%28SDL_Event%29
[2]
SDL Keyboard事件: https://wiki.libsdl.org/SDL_KeyboardEvent
[3]
SDL Keysym: https://wiki.libsdl.org/SDL_Keysym
[4]
SDL Keycode: https://wiki.libsdl.org/SDL_Keycode
[5]
这里: http://www.lazyfoo.net/tutorials/SDL/04_key_presses/04_key_presses.zip
[6]
原文链接: http://www.lazyfoo.net/tutorials/SDL/04_key_presses/index.php