对于窗口程序来说,消息循环和事件响应是非常重要的,这节将介绍如何利用SDL提供的API函数实现消息循环和键盘ESC键响应。
将上节中的这部分代码注释掉:
//if( SDL_Flip( screen ) == -1 ) //{ // return 1; //} //SDL_Delay( 2000 );
上面的代码主要是将内存中的内容显示到屏幕上,然后等待两秒,让用户有机会看到图片。
下面要将这部分带代码替换成一个消息循环,让程序可以响应用户消息,代码如下:
bool bQuit = false; //程序是否退出的标志 SDL_Event event; while(!bQuit) //如果bQuit不为true的话,循环一直进行 { //从消息循环中尝试取出一个消息,如果有的话返回ture,否则返回false //SDL_PollEvent不会等待,而是立刻返回 while( SDL_PollEvent( &event ) ) { switch(event.type) //处理取到的消息,判断消息类型 { case SDL_KEYDOWN: //如果是键盘按下事件 if(event.key.keysym.sym == SDLK_ESCAPE) //如果按的是ESC键 { bQuit = true; //退出循环 } break; case SDL_QUIT: //如果是窗口关闭事件,例如点击窗口的关闭按钮 bQuit = true; //退出循环 break; default: break; } } //将内存中的内容显示到屏幕上,该函数每调用一次就会更新一次窗口的内容,即游戏中的一帧 //由于在这个循环中我们没有修改内存中的内容,所以屏幕窗口中显示的内容不会发生变化 if( SDL_Flip( screen ) == -1 ) { break; } }
学过windwos API编程的人一定对上面的这段代码非常熟悉,因为它和windows的消息循环机制很相似,对于windows的消息循环来说我们一般这样写:
MSG msg; while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
在这里windows和SDL消息循环的区别在于GetMessage在消息队列中没有消息的情况下会等待,直到有消息为止;而SDL_PollEvent是不管有没有消息都马上返回。
其实在windows api中也有与SDL的SDL_PollEvent对应的函数,那就是PeekMessage。同样在SDL中也有与GetMessage对于的SDL_WaitEvent函数。
在这里,我们为什么用SDL_PollEvent而不用SDL_WaitEvent?是因为SDL_WaitEvent在没有用户消息的时候会造成程序等待,从而影响到游戏的帧数,这样在有动画的游戏中,就会出现用户不操作的时候界面停止,当用户再次操作时,界面发生跳跃的情况。试想玩俄罗斯方块的时候,你操作一下它就动一下,你不操作他就不动;或者刚刚方块还在上面,你等了一下没操作键盘,等你再操作键盘的时候,方块已经到下面了,这个是因为方块的下落一般是在定时器中处理的,是后台处理,没有用户消息的时候,后台的定时器还在运行,但界面没有和后台同步,从而造成画面跳跃前进的状况。
对于消息处理来说,SDL还提供了一些函数,由于我们暂时对消息处理没有进一步更深的需求,所以在这里不详细介绍,等用到的时候再说。如果你想了解一下的话,可以阅读SDL的文档。
下面分析SDL_Event的结构:
typedef union SDL_Event { Uint8 type; //消息类型 SDL_ActiveEvent active; //窗口焦点、输入焦点及鼠标焦点的失去和得到消息 SDL_KeyboardEvent key; //键盘消息,键盘按下和释放 SDL_MouseMotionEvent motion;//鼠标移动消息 SDL_MouseButtonEvent button;//鼠标按键消息 SDL_JoyAxisEvent jaxis; //手柄消息 SDL_JoyBallEvent jball; //手柄消息 SDL_JoyHatEvent jhat; //手柄消息 SDL_JoyButtonEvent jbutton; //手柄消息 SDL_ResizeEvent resize; //窗口大小变化事件 SDL_ExposeEvent expose; //窗口重绘消息 SDL_QuitEvent quit; //退出消息 SDL_UserEvent user; //用户自定义事件 SDL_SysWMEvent syswm; //平台相关的系统消息 } SDL_Event;
SDL_Event是一个联合体,type字段决定了其中的哪个结构体数据有效。
本节中只用到了键盘消息,所以这里主要介绍键盘消息,如果后面用到了其他消息,将做详细解释。SDL_KeyboardEvent定义:
typedef struct { Uint8 type; //消息类型,SDL_KEYDOWN/UP Uint8 state; //键盘状态,表示按下还是释放,在这里和type字段代表的意义相同 SDL_keysym keysym; //具体的按键信息 } SDL_KeyboardEvent;
这里是键盘消息结构体的定义,type表示类型,由于SDL_Event是联合体,所以SDL_KeyboardEvent中的type等于SDL_Event中的type字段。下面看SDL_keysym:
typedef struct { Uint8 scancode; //按键对应的硬件扫描码 SDLKey sym; //按键对应的SDL枚举定义,SDL定义的键盘键以SDLK_开头(如SDLK_DELETE表示删除键) SDLMod mod; //哪个辅助键被按下,辅助键指ALT,SHIFT之类的键 Uint16 unicode; //输入对应的unicode编码(需要调用SDL_EnableUNICODE开启unicode,SDL默认不开启) } SDL_keysym;
其中scancode 暂时用不着,判断哪个键被按下一般都是用sym ,SDLKey和SDLMod都是SDL定义的枚举,在头文件SDL_keysym.h中。具体的可参考SDL文档或者源代码。
到这里为止,我们可以在窗口中显示一张图片,并且可以移动和关闭窗口了,在这过程中用到的函数的具体相关细节,需要大家自己去尝试,去了解,这里只是介绍了一些常用的功能。下一节中将介绍如何用C++将这些代码封装起来。