前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >(译)SDL编程入门(17)鼠标事件

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

作者头像
arcticfox
发布2020-10-29 10:13:24
1.5K0
发布2020-10-29 10:13:24
举报

鼠标事件

和按键一样,SDL也有事件结构来处理鼠标事件,如鼠标运动、鼠标按钮按下和鼠标按钮释放。在本教程中,我们将制作一堆可以与之交互的按钮。

代码语言:javascript
复制
//按钮常量
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个按钮。根据鼠标移动到、点击、释放或移出按钮,我们将显示不同的精灵。这些常量就是用来定义这一切的。

代码语言:javascript
复制
//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没有被定义,忽略这段代码。

代码语言:javascript
复制
//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;
};

这里是表示一个按钮的类,它有一个初始化的构造函数、一个位置设置器、一个事件循环的事件处理程序和一个渲染函数。 它还具有一个位置和一个精灵枚举,所以我们知道要为按钮渲染哪个精灵。

代码语言:javascript
复制
#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进行编译,这里再次将字体函数的加载夹在另一个定义好的条件下。

代码语言:javascript
复制
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;
}

这里是按钮的构造函数和一个位置设置函数。正如你所看到的,它们初始化了默认的精灵并设置了位置。

代码语言:javascript
复制
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。 否则,它将保持初始真实值。

最后,我们根据鼠标是否位于按钮内以及鼠标事件来设置按钮精灵。

如果鼠标不在按钮内,则将鼠标设置为精灵。如果鼠标不在按钮内部,我们设置鼠标出精灵。如果鼠标在按钮内部,我们设置的精灵是在鼠标移动时鼠标在上,鼠标按下时鼠标在下,鼠标释放时鼠标在上。

代码语言:javascript
复制
void LButton::render(){
    //显示当前按钮的精灵
    gButtonSpriteSheetTexture.render( mPosition.x, mPosition.y, &gSpriteClips[ mCurrentSprite ] );
}

在渲染函数中,我们只是在按钮位置渲染当前的按钮精灵。

代码语言:javascript
复制
//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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程之路从0到1 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 鼠标事件
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档