Отслеживаем события мыши в SDL

Практически в любой игре необходимо отслеживать куда, зачем и как пользователь «ткнул» мышью. Собственно этим и займёмся..

Сразу оговорюсь, что эта статья всего лишь свободный, слегка преобразованный перевод этой статьи — https://lazyfoo.net/SDL_tutorials/lesson09/index.php
Так что если вы владеете английским на уровне чтения документации, то предлагаю воспользоваться первоисточником..

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

Постараюсь освятить все основные моменты, если что не ясно милости просим в коментарии 😉

Заголовки:

#include «SDL/SDL.h»

#include «SDL/SDL_image.h»

#include <string>

.. конечно необходимо предварительно установить библиотеку SDL, SDL_image

 

Параметры экрана:

const int SCREEN_WIDTH = 640;  // ширина

const int SCREEN_HEIGHT = 480;  // высота

const int SCREEN_BPP = 32;  // цветность

Состояния мыши в виде констант:

const int CLIP_MOUSEOVER = 0;

const int CLIP_MOUSEOUT = 1;

const int CLIP_MOUSEDOWN = 2;

const int CLIP_MOUSEUP = 3;

Поверхности для изображений:

SDL_Surface *buttonSheet = NULL;

SDL_Surface *screen = NULL;

Переменная для отслеживания событий:

SDL_Event event;

Массив размеров для изображений:

SDL_Rect clips[ 4 ];

Класс для работы с мышкой:

class Button{

    private:

    SDL_Rect box;  // атрибуты кнопки (местоположение)

    SDL_Rect* clip;  // часть спрайта которая будет показана

    public:

    Button( int x, int y, int w, int h );  // конструктор

    void handle_events();  // обработка событий

    void show();  // показывает «кнопку» на экране

};

На этом предопределения и глобальные переменные закончились. Приступим к созданию функций.

Функция загрузки изображения (принимает имя файла):

SDL_Surface *load_image( std::string filename ){

    SDL_Surface* loadedImage = NULL;

    SDL_Surface* optimizedImage = NULL;

    loadedImage = IMG_Load( filename.c_str() );  // загружает картинку

    if( loadedImage != NULL ){  // если загрузили

        optimizedImage = SDL_DisplayFormat( loadedImage );  // оптимизируем изображение под наш экран

        SDL_FreeSurface( loadedImage );  // чистим старое

        if( optimizedImage != NULL ){  /// устанавливаем прозрачный цвет

            SDL_SetColorKey( optimizedImage, SDL_SRCCOLORKEY, SDL_MapRGB( optimizedImage->format, 0, 0xFF, 0xFF ) );

        }

    }

    return optimizedImage;  // вернём оптимизированное изображение

}

Функция отрисовки спрайта в заданную часть экрана:

void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL )
{

    SDL_Rect offset;

    offset.x = x;

    offset.y = y;

    SDL_BlitSurface( source, clip, destination, &offset );

}

Тут всё стандартно.. если что читайте статью о спрайтах в SDL.

Функция инициализации библиотеки:

bool init(){

    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 ) {  // вкл все системы

        return false;

    }

    screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );  // настраиваем экран

    if( screen == NULL ){

        return false;

    }

    SDL_WM_SetCaption( «Button Test», NULL );  // заголовок окна

    return true;

}

Функция загрузки файлов:

bool load_files(){

    buttonSheet = load_image( «button.png» );

    if( buttonSheet == NULL ){

        return false;

    }

    return true;

}

По сути это оболочка для удобного использования load_image()

Очистка при завершении программы:

void clean_up(){

    SDL_FreeSurface( buttonSheet );

    SDL_Quit();

}

Разбивка спрайта на клипы (clips):

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;

}

Это сделано из-за того, что входной файл состоит из 4 смежных изображений (см. в архиве с исходниками ниже)

Конструктор класса работы с «кнопкой»:

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 ];

}

Теперь посмотрим на обнаружение событий:

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 ];  // иначе не на кнопке

        }

    }

 // если была нажата кнопка

    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 ];  // ставим клип с сообщением о том, что кнопка нажата

            }

        }

    }

 // если кнопка была отжата

    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 ];

            }

        }

    }

}

Как видите столь громоздкая функция — совсем не сложна.

Метод показа кнопки:

void Button::show(){

    apply_surface( box.x, box.y, buttonSheet, screen, clip );

}

Осталось только вызвать всё это дело. main:

int main( int argc, char* args[] ){

    bool quit = false;  // флаг выхода

    if( init() == false ){  // инициализация

        return 1;

    }

    if( load_files() == false ){  // загрузка файлов

        return 1;

    }

    set_clips();  // установка положения клипов на спрайте

    Button myButton( 170, 120, 320, 240 );  // кнопка

    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;

        }

    }

    clean_up();  // чистимся

    return 0;

}

Всё довольно просто. Скачать исходник и спрайт.

 

Похожий код:

Фото аватара
Алексей Петров

Программист, разработчик с 5 летним опытом работы. Учусь на разработчика игр на Unity и разработчика VR&AR реальности (виртуальной реальности). Основные языки программирования: C#, C++.

Оцените автора
Бла, бла код
Добавить комментарий