Событийно-ориентированное программирование
До этого момента, вы, вероятно, писали программы в "командно-ориентированном" стиле, используя cin
и cout
. Эта руководство научит вас как проверять и обрабатывать события.
В оригинале автор использует термин "X out". Как я понял под этим подразумевается закрытие приложение, либо при помощи нажатия на значок крестика в заголовке окна, либо при помощи соответствующего клавиатурного сочетания, например, Alt + F4
. Я буду называть это действие "закрытие приложения"
Событие это просто что-то, что произошло. Это может быть нажатие клавиши, движение мыши, изменение размера окна или в нашем случае, желание пользователя закрыть окно.
//Заголовки
#include
#include
#include
//Аттрибуты экрана
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;
//Поверхности, которые будут использоваться
SDL_Surface *image = NULL;
SDL_Surface *screen = NULL;
Здесь у нас все тоже самое что и раньше: подключение заголовочных файлов и объявление констант и структур для поверхностей.
//Структура для событий, которые будут использоваться
SDL_Event event;
А вот это уже кое-что новенькое. Структура SDL_Event
хранит данные события, которые мы будем обрабатывать.
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 );
}
//Вернуть оптимизированное изображение
return optimizedImage;
}
void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination ) {
//Делаем прямоугольник, для временного хранения смещений
SDL_Rect offset;
//Кладем смещения в прямоугольник
offset.x = x;
offset.y = y;
//Копировать поверхность
SDL_BlitSurface( source, NULL, destination, &offset );
}
Это наши функции для загрузки и копирования поверхностей. По сравнению с прошлой частью руководства ничего не поменялось.
bool init() {
//Инициализировать все подсистемы SDL
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( "Event test", NULL );
//Если все прошло хорошо
return true;
}
Это наша функция инициализации. Она запускает SDL
, подготавливает окно, устанавливает заголовок и возвращает false
если происходит какая-нибудь ошибка.
bool load_files() {
//Загрузить изображение
image = load_image( "x.png" );
//Если при загрузке произошла ошибка
if( image == NULL ) {
return false;
}
//Если все загрузилось
return true;
}
Это функция загружающая файлы. Она загружает изображения и возвращает false
если возникли какие-то проблемы.
void clean_up() {
//Освободить поверхность
SDL_FreeSurface( image );
//Завершаем SDL
SDL_Quit();
}
Здесь у нас функция очистки, вызываемая при завершении программы. Она освобождает нашу поверхность и завершает работу SDL
.
int main( int argc, char* args[] ) {
//Подтверждение того, что программа ожидает выхода
bool quit = false;
Теперь мы дошли до нашей функции main
Здесь мы объявляем переменную quit
, которая следит за тем, желает ли пользователь покинуть программу. Так как приложение только что запустилось, мы устанавливаем ее в false
, иначе завершение работы произойдет немедленно.
//Инициализация
if( init() == false ) {
return 1;
}
//Загрузить файлы
if( load_files() == false ) {
return 1;
}
Теперь мы вызываем функции инициализации и загрузки файлов, сделанные ранее.
//Скопировать поверхность на экран
apply_surface( 0, 0, image, screen );
//Обновить экран
if( SDL_Flip( screen ) == -1 ) {
return 1;
}
Затем мы показываем изображение на экране.
//До тех пор, пока пользователь не захотел закрыть приложение
while( quit == false ) {
Теперь мы запускаем главный цикл программы. Этот цикл выполняется до тех пор, пока пользователь не установит quit
в true
.
//Пока есть события для обработки
while( SDL_PollEvent( &event ) ) {
В SDL
, когда происходит событие, она попадает в "очередь событий". Очередь событий хранит данные для каждого произошедшего события. Поэтому если вы нажали кнопку мыши, передвинули мышь, а затем нажали на клавишу на клавиатуре, очередь событий будет выглядеть как-то так:SDL_PollEvent()
занимается тем, что берет событие из очереди и помещает его данные в нашу структуру:В результате этот код продолжает получать данные событий, пока очередь не опустеет.
//Если пользователь попытался закрыть окно
if( event.type == SDL_QUIT ) {
//Закрываем приложение
quit = true;
}
}
}
Когда пользователь попытается закрыть приложение, тип полученного события будет SDL_QUIT
. Но когда пользователь делает это, программа не закрывается; это только сообщает нам, что пользователь хочет выйти из программы. А поскольку мы хотим чтобы программа завершилась, когда пользователь попытается её закрыть, мы устанавливаем quit
в true
и это прерывает цикл, в котором мы находимся.
//Освободить поверхность и завершить работу SDL
clean_up();
return 0;
}
В конце концов, мы выполняем функцию очистки. Есть и другие способы обработки событий, например, SDL_WaitEvent()
и SDL_PeepEvents()
. Вы можете узнать о них в документации.
Скачать исходники
Примечание: теперь самое время узнать побольше о функциях SDL
по обработке ошибок. У меня нет руководства по ним, но я затрагиваю их в статье 5 (англ.). В документация по SDL
объясняется функция SDL_GetError()
, а документация по SDL_image
объясняет IMG_GetError()
. SDL_ttf
и SDL_mixer
так же имеют свои функции обработки ошибок, так что не забудьте посмотреть и их.
Допольнительные материалы:
Cобытийно-ориентированное программирование (Event Driven Programming)