和按键一样,SDL也有事件结构来处理鼠标事件,如鼠标运动、鼠标按钮按下和鼠标按钮释放。在本教程中,我们将制作一堆可以与之交互的按钮。
//按钮常量
const int BUTTON_WIDTH = 300;
const int BUTTON_HEIGHT = 200;
const int TOTAL_BUTTONS = 4;
enum LButtonSprite
{
BUTTON_SPRITE_MOUSE_OUT = 0,
BUTTON_SPRITE_MOUSE_OVER_MOTION = 1,
BUTTON_SPRITE_MOUSE_DOWN = 2,
BUTTON_SPRITE_MOUSE_UP = 3,
BUTTON_SPRITE_TOTAL = 4
};
在本教程中,我们将在屏幕上显示4个按钮。根据鼠标移动到、点击、释放或移出按钮,我们将显示不同的精灵。这些常量就是用来定义这一切的。
//Texture wrapper class
class LTexture
{
public:
//Initializes variables
LTexture();
//Deallocates memory
~LTexture();
//Loads image at specified path
bool loadFromFile( std::string path );
#if defined(SDL_TTF_MAJOR_VERSION)
//Creates image from font string
bool loadFromRenderedText( std::string textureText, SDL_Color textColor );
#endif
//Deallocates texture
void free();
//Set color modulation
void setColor( Uint8 red, Uint8 green, Uint8 blue );
//Set blending
void setBlendMode( SDL_BlendMode blending );
//Set alpha modulation
void setAlpha( Uint8 alpha );
//Renders texture at given point
void render( int x, int y, SDL_Rect* clip = NULL, double angle = 0.0, SDL_Point* center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE );
//Gets image dimensions
int getWidth();
int getHeight();
private:
//The actual hardware texture
SDL_Texture* mTexture;
//Image dimensions
int mWidth;
int mHeight;
};
我们正在对纹理类进行轻微的修改。在本教程中,我们不会使用SDL_ttf来渲染文本。这意味着我们不需要loadFromRenderedText函数。与其删除我们将来可能需要的代码,不如将它包在if定义的语句中,这样如果我们不包含SDL_ttf,编译器将忽略它。它检查SDL_TTF_MAJOR_VERSION宏是否被定义。和#include一样,#if也是一个宏,用来和编译器对话。在这种情况下,它说如果SDL_ttf没有被定义,忽略这段代码。
//The mouse button
class LButton
{
public:
//Initializes internal variables
LButton();
//设置左上角位置
void setPosition( int x, int y );
//处理鼠标事件
void handleEvent( SDL_Event* e );
//显示按钮精灵
void render();
private:
//左上角位置
SDL_Point mPosition;
//当前使用的全局精灵
LButtonSprite mCurrentSprite;
};
这里是表示一个按钮的类,它有一个初始化的构造函数、一个位置设置器、一个事件循环的事件处理程序和一个渲染函数。 它还具有一个位置和一个精灵枚举,所以我们知道要为按钮渲染哪个精灵。
#if defined(SDL_TTF_MAJOR_VERSION)
bool LTexture::loadFromRenderedText( std::string textureText, SDL_Color textColor ){
//Get rid of preexisting texture
free();
//Render text surface
SDL_Surface* textSurface = TTF_RenderText_Solid( gFont, textureText.c_str(), textColor );
if( textSurface == NULL ){
printf( "Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError() );
}else{
//Create texture from surface pixels
mTexture = SDL_CreateTextureFromSurface( gRenderer, textSurface );
if( mTexture == NULL ){
printf( "Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError() );
}else{
//Get image dimensions
mWidth = textSurface->w;
mHeight = textSurface->h;
}
//Get rid of old surface
SDL_FreeSurface( textSurface );
}
//Return success
return mTexture != NULL;
}
#endif
为了确保我们的源代码不使用SDL_ttf进行编译,这里再次将字体函数的加载夹在另一个定义好的条件下。
LButton::LButton(){
mPosition.x = 0;
mPosition.y = 0;
mCurrentSprite = BUTTON_SPRITE_MOUSE_OUT;
}
void LButton::setPosition( int x, int y ){
mPosition.x = x;
mPosition.y = y;
}
这里是按钮的构造函数和一个位置设置函数。正如你所看到的,它们初始化了默认的精灵并设置了位置。
void LButton::handleEvent( SDL_Event* e ){
//如果发生了鼠标事件
if( e->type == SDL_MOUSEMOTION || e->type == SDL_MOUSEBUTTONDOWN || e->type == SDL_MOUSEBUTTONUP )
{
//获取鼠标位置
int x, y;
SDL_GetMouseState( &x, &y );
//检查鼠标是否在按钮上
bool inside = true;
//鼠标在按钮左边
if( x < mPosition.x )
{
inside = false;
}
//鼠标在按钮的右边
else if( x > mPosition.x + BUTTON_WIDTH )
{
inside = false;
}
//鼠标在按钮上方
else if( y < mPosition.y )
{
inside = false;
}
//鼠标在按钮下方
else if( y > mPosition.y + BUTTON_HEIGHT )
{
inside = false;
}
//鼠标在按钮外
if( !inside )
{
mCurrentSprite = BUTTON_SPRITE_MOUSE_OUT;
}
//鼠标在按钮内
else
{
//Set mouse over sprite
switch( e->type )
{
case SDL_MOUSEMOTION:
mCurrentSprite = BUTTON_SPRITE_MOUSE_OVER_MOTION;
break;
case SDL_MOUSEBUTTONDOWN:
mCurrentSprite = BUTTON_SPRITE_MOUSE_DOWN;
break;
case SDL_MOUSEBUTTONUP:
mCurrentSprite = BUTTON_SPRITE_MOUSE_UP;
break;
}
}
}
}
下面是本教程的重点,我们将处理鼠标事件。这个函数将在事件循环中被调用,并处理从事件队列中获取的单个按钮的事件。
首先,我们检查进入的事件是否是一个鼠标事件,特别是鼠标运动事件(当鼠标移动时),鼠标按钮按下事件(当你点击鼠标按钮时),或鼠标按钮抬起事件(当你释放鼠标点击时)。
如果这些鼠标事件确实发生了,我们就使用SDL_GetMouseState检查鼠标位置。根据鼠标是否在按钮上,我们要显示不同的精灵。
在这里,我们要检查鼠标是否在按钮内。 由于我们对SDL使用了不同的坐标系,因此按钮的原点位于左上方。 这意味着每个小于x位置的x坐标都在按钮的外部,每个小于y位置的y坐标也都在按钮之外。 按钮右侧的所有内容均为x位置+宽度,按钮下方的所有内容均为y位置+高度。
这就是这段代码的作用。 如果鼠标位置在按钮之外,则它将内部标记标记为false。 否则,它将保持初始真实值。
最后,我们根据鼠标是否位于按钮内以及鼠标事件来设置按钮精灵。
如果鼠标不在按钮内,则将鼠标设置为精灵。如果鼠标不在按钮内部,我们设置鼠标出精灵。如果鼠标在按钮内部,我们设置的精灵是在鼠标移动时鼠标在上,鼠标按下时鼠标在下,鼠标释放时鼠标在上。
void LButton::render(){
//显示当前按钮的精灵
gButtonSpriteSheetTexture.render( mPosition.x, mPosition.y, &gSpriteClips[ mCurrentSprite ] );
}
在渲染函数中,我们只是在按钮位置渲染当前的按钮精灵。
//While application is running
while( !quit )
{
//Handle events on queue
while( SDL_PollEvent( &e ) != 0 )
{
//User requests quit
if( e.type == SDL_QUIT )
{
quit = true;
}
//Handle button events
for( int i = 0; i < TOTAL_BUTTONS; ++i )
{
gButtons[ i ].handleEvent( &e );
}
}
//Clear screen
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer );
//Render buttons
for( int i = 0; i < TOTAL_BUTTONS; ++i )
{
gButtons[ i ].render();
}
//Update screen
SDL_RenderPresent( gRenderer );
}
这是我们的主循环。在事件循环中,我们处理退出事件和所有按钮的事件。在渲染部分,所有的按钮都被渲染到屏幕上。
还有鼠标滚轮事件[1],这里没有讲到,但如果你看一下文档,玩一玩,应该不难弄明白。
在 这里[2]下载本教程的媒体和源代码。
原文链接[3]
[1]
鼠标滚轮事件: http://wiki.libsdl.org/SDL_MouseWheelEvent
[2]
这里: http://www.lazyfoo.net/tutorials/SDL/17_mouse_events/17_mouse_events.zip
[3]
原文链接: http://www.lazyfoo.net/tutorials/SDL/17_mouse_events/index.php