SDL编程入门(17)鼠标事件

鼠标事件

和按键一样,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 );
}

这是我们的主循环。在事件循环中,我们处理退出事件和所有按钮的事件。在渲染部分,所有的按钮都被渲染到屏幕上。

还有鼠标滚轮事件,这里没有讲到,但如果你看一下文档,玩一玩,应该不难弄明白。

这里下载本教程的媒体和源代码。

原文链接

关注我的公众号:编程之路从0到1
编程之路从0到1

血色v残阳 CSDN认证博客专家 编程达人
掌握C、Java、Python、Go、Dart语言等多种编程语言,擅长GUI开发,曾从事Android原生开发,Flutter跨平台开发等等工作
微信公众号:
“编程之路从0到1”
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页