События мыши


Пришло время научится обрабатывать события от мышки. В этом простом туториале мы будем учится обрабатывать различные события мыши, с целью сделать простую кнопку.

//Кнопка
class Button {
private:
    //Атрибуты кнопки
    SDL_Rect box;
    //Часть спрайт листа кнопки, которая будет отображаться
    SDL_Rect* clip;
public:
    //Инициализация переменных
    Button( int x, int y, int w, int h );
    //Обработка событий и выбор спрайта для отображения
    void handle_events(); 
    //Вывод кнопки на экран
    void show();
};

Это наш класс для кнопки, с которым мы будем взаимодействовать.

В нем есть прямоугольник определяющий позицию и размеры кнопки. Так же у нас есть указатель на спрайт из спрайт листа, которым пользуется кнопка.

Затем у нас есть конструктор, устанавливающий атрибуты кнопки в соответствие с переданными аргументами.
Далее следует функция handle_events(), которая обрабатывает движение и другие события мыши.

И наконец функция show() отображающая кнопку на экран.

void set_clips()
{
    //нарезаем спрайты
    clips[ CLIP_MOUSEOVER ].x = 0;
    clips[ CLIP_MOUSEOVER ].y = 0;
    clips[ CLIP_MOUSEOVER ].w = 320;
    clips[ CLIP_MOUSEOVER ].h = 240;
 
    clips[ CLIP_MOUSEOUT ].x = 320;
    clips[ CLIP_MOUSEOUT ].y = 0;
    clips[ CLIP_MOUSEOUT ].w = 320;
    clips[ CLIP_MOUSEOUT ].h = 240;
 
    clips[ CLIP_MOUSEDOWN ].x = 0;
    clips[ CLIP_MOUSEDOWN ].y = 240;
    clips[ CLIP_MOUSEDOWN ].w = 320;
    clips[ CLIP_MOUSEDOWN ].h = 240;
 
    clips[ CLIP_MOUSEUP ].x = 320;
    clips[ CLIP_MOUSEUP ].y = 240;
    clips[ CLIP_MOUSEUP ].w = 320;
    clips[ CLIP_MOUSEUP ].h = 240;    
}

Это наша функция, вырезающая изображения из листа спрайтов:

Как вы можете видеть, у нас есть спрайт для различных событий мыши. И у нас есть массив из четырех SDL_Rect‘s который выкраивает спрайты для каждого состояния кнопки. Каждый спрайт кнопки имеет соответствующую константу.

Button::Button( int x, int y, int w, int h )
{
    //Установить атрибуты
    box.x = x;
    box.y = y;
    box.w = w;
    box.h = h;
 
    //Спрайт по умолчанию
    clip = &clips[ CLIP_MOUSEOUT ];
}

Конструктор класса кнопки довольно прост. Он устанавляет смещения кнопки по x и y, а так же ширину и высоту.
Так же он устанавливает спрайт по умолчанию.

void Button::handle_events()
{
    //Смещения мыши
    int x = 0, y = 0;
 
    //Если мышь сдвинулась
    if( event.type == SDL_MOUSEMOTION )
    {
        //Получить смещения
        x = event.motion.x;
        y = event.motion.y;
 
        //Если мышь над кнопкой
        if( ( x > box.x ) && ( x < box.x + box.w ) &&
                ( y > box.y ) && ( y < box.y + box.h ) )
        {
            //Установить соответсвующий спрайт
            clip = &clips[ CLIP_MOUSEOVER ];    
        }
        //Если нет
        else
        {
            //Ставим спрайт
            clip = &clips[ CLIP_MOUSEOUT ];
        }    
    }

В обработчике событий, перво-наперво мы проверяем двигалась ли мышь. Когда мышь движется, происходит событие SDL_MOUSEMOTION

Если мышь двинулась, мы получаем ее смещения из структуры события, затем проверяем находится ли она над кнопкой. Если она над ней, мы устанавливаем спрайт Mouse over, или спрайт Mouse out в противном случае.

    //Если была нажата кнопка мыши
    if( event.type == SDL_MOUSEBUTTONDOWN )
    {
        //Если нажили левую
        if( event.button.button == SDL_BUTTON_LEFT )
        {
            //Получить смещения
            x = event.button.x;
            y = event.button.y;
 
            //Если мышь над кнопкой
            if( ( x > box.x ) && ( x < box.x + box.w ) &&
                    ( y > box.y ) && ( y < box.y + box.h ) )
            {
                //Установить спрайт
                clip =&clips[ CLIP_MOUSEDOWN ];
            }
        }
    }

Затем проверяем было ли нажатие кнопок мыши. При нажатии происходит событие SDL_MOUSEBUTTONDOWN.

Мы хотим, чтобы кнопка реагировала только на левую кнопку мыши, так что мы проверяем была ли нажата именно левая кнопка мыши.

Далее проверяем было ли нажатие над нашей кнопкой. Если это так, то устанавливаем для кнопки спрайт Mouse down

    //Если кнопку мыши отпустили
    if( event.type == SDL_MOUSEBUTTONUP )
    {
        //Если отпустили левую кнопку
        if( event.button.button == SDL_BUTTON_LEFT )
        { 
            //получить смещения
            x = event.button.x;
            y = event.button.y;
 
            //Если мышь над кнопкой
            if( ( x > box.x ) && ( x < box.x + box.w ) &&
                    ( y > box.y ) && ( y < box.y + box.h ) )
            {
                //Установить спрайт
                clip = &clips[ CLIP_MOUSEUP ];
            }
        }
    }
}

Затем мы проверяем была ли кнопка мыши отпущена над кнопкой с событием SDL_MOUSEBUTTONUP

В этой программе мы получаем смещения мыши из структуры события. Было бы более целесообразно получать смещения мыши при помощи SDL_GetMouseState(), но… я слишком ленив, чтобы возвращаться назад и переписывать код.

void Button::show()
{
    //Показать кнопку
    apply_surface( box.x, box.y, buttonSheet, screen, clip );
}

Далее в функции show мы отображаем спрайт кнопки на экране.

    //Нарезать спрайт лист
    set_clips();
    //Сделать кнопку
    Button myButton( 170, 120, 320, 240 );

В начале функции main() после инициализации и загрузки всего что нужно, мы нарезаем спрайт лист и настраиваем нашу кнопку.

    //Пока пользователь не захотел выйти
    while( quit == false )
    {
        //Если есть события для обработки
        if( SDL_PollEvent( &event ) )
        {
            //Обработать события мыши
            myButton.handle_events();
 
            //Если пользователь пытается закрыть приложение
            if( event.type == SDL_QUIT )
            {
                //Завершить программу
                quit = true;
            }    
        }
 
        //Залить экран белым
        SDL_FillRect( screen, &screen->clip_rect,
                         SDL_MapRGB( screen->format, 0xFF, 0xFF, 0xFF ) );
 
        //Показать кнопку
        myButton.show();
 
        //Обновить экран
        if( SDL_Flip( screen ) == -1 )
        {
            return 1;    
        }
    }

Здесь мы видим наш класс кнопки в действии внутри главного цикла.

В начале мы обрабатываем события. Как вы можете видеть мы используем обработчик событий кнопки, а так же проверяем не хочет ли пользователь завершить приложение.

Обычно для обработки событий используется цикл while, но в этом (и в предыдущих) мы используем if. Из-за этого обрабатывается одно событие за кадр — таким образом легче видеть отдельные события. В большинстве настоящих приложений используется while, потому что обычно вы хотите обработать все события в очереди в каждом кадре.

После обработки события, мы очищаем экран, заливая его белым. Затем мы отображаем кнопку и обновляем экран.

И далее главный цикл повторяется, таким образом мы можем отрисовать следующий кадр, пока пользователь не захочет выйти.

Если у вас быстрый компьютер, вы скорее всего не увидите спрайт CLIP_MOUSEUP. Это происходит потому, что приложении работает так быстро, что он отображается только долю секунды. К счастью, далее будет группа туториалов, показывающих как рассчитывать время и регулировать скорость кадров. Если вы замедлите выполнение программы до 20 кадров в секунду у вы хотя бы сможете заметить его.


Скачать исходники

События мыши: 8 комментариев

  1. Никита

    А почему не работает
    *clip = clips[ CLIP_MOUSEOVER ]; //Значение по указателю *clip изменяем на 0
    вместо
    clip = &clips[ CLIP_MOUSEOVER ]; //В клип помещаем адрес, где хранится 0

    1. admin Автор записи
      SDL_Rect* clip;

      clip — неинициализированный указатель. Присвоение в *clip приводит к неопределенному поведению.
      apply_surface использует адрес указателя, а не его значение.

  2. dima

    если зажать кнопку мыши на кнопке, и не отпуская начать двигать мышь в пределах кнопки — спрайт меняется на Mouse Over. никак не пойму что нужно сделать для того, чтобы спрайт оставался Mouse down?

    1. admin Автор записи

      Да, так и должно быть, т.к. текущий клип меняется только при наступлении какого-либо события, будь-то нажатие или движение мыши.

Добавить комментарий