Pull to refresh

Перевод SDL Game Framework Series. Часть 3 — SDL Events

Reading time 7 min
Views 11K
Original author: Tim Jones
Одной из основ разработки игр является обработка сообщений поступающих во время каких-либо событий (Events). Все видеоигры, от тенниса до очень сложных игр для ПК и консолей, используют т.н. события для взаимодействия с игроком. Эти события могут поступать от клавиатур, мышей, джойстиков, геймпадов, и т.д., а также от самой операционной системы. Важно понимать, как они работают, если мы хотим надлежащим образом обрабатывать взаимодействие пользователя с игрой. Мы уже использовали события, но только для закрытия нашего окна, теперь мы будем глубже разбираться с тем, как получать события от пользователя.

Как вы уже заметили, каждый урок построен на основе предыдущего, поэтому мы не будем пока отступать от этой традиции. Для того, чтобы отслеживать все события и обрабатывать их в предназначенных для этого функциях, нужно создать новый класс. Добавьте в проект два файла CEvent.h и CEvent.cpp. В этих двух файлах мы будем обрабатывать приходящие сообщения, и вызвать соответствующую функцию. Наш класс CApp будет наследоваться от этого класса, поэтому, когда мы начнем обрабатывать события, просто будем переопределять его функции.

Откройте CEvent.h и добавьте следующий код:

CEvent.h
#ifndef _CEVENT_H_
    #define _CEVENT_H_
 
#include <SDL.h>
 
class CEvent {
    public:
        CEvent();
 
        virtual ~CEvent();
 
        virtual void OnEvent(SDL_Event* Event);
 
        virtual void OnInputFocus();
 
        virtual void OnInputBlur();
 
        virtual void OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode);
 
        virtual void OnKeyUp(SDLKey sym, SDLMod mod, Uint16 unicode);
 
        virtual void OnMouseFocus();
 
        virtual void OnMouseBlur();
 
        virtual void OnMouseMove(int mX, int mY, int relX, int relY, bool Left,bool Right,bool Middle);
 
        virtual void OnMouseWheel(bool Up, bool Down);    //Not implemented
 
        virtual void OnLButtonDown(int mX, int mY);
 
        virtual void OnLButtonUp(int mX, int mY);
 
        virtual void OnRButtonDown(int mX, int mY);
 
        virtual void OnRButtonUp(int mX, int mY);
 
        virtual void OnMButtonDown(int mX, int mY);
 
        virtual void OnMButtonUp(int mX, int mY);
 
        virtual void OnJoyAxis(Uint8 which, Uint8 axis, Sint16 value);
 
        virtual void OnJoyButtonDown(Uint8 which, Uint8 button);
 
        virtual void OnJoyButtonUp(Uint8 which, Uint8 button);
 
        virtual void OnJoyHat(Uint8 which, Uint8 hat, Uint8 value);
 
        virtual void OnJoyBall(Uint8 which, Uint8 ball, Sint16 xrel, Sint16 yrel);
 
        virtual void OnMinimize();
 
        virtual void OnRestore();
 
        virtual void OnResize(int w,int h);
 
        virtual void OnExpose();
 
        virtual void OnExit();
 
        virtual void OnUser(Uint8 type, int code, void* data1, void* data2);
};
 
#endif


Нехилый получился класс? Ну а теперь откройте CEvent.cpp, и добавьте следующий код:

CEvent.cpp
#include "CEvent.h"
 
CEvent::CEvent() {
}
 
CEvent::~CEvent() {
    //Do nothing
}
 
void CEvent::OnEvent(SDL_Event* Event) {
    switch(Event->type) {
        case SDL_ACTIVEEVENT: {
            switch(Event->active.state) {
                case SDL_APPMOUSEFOCUS: {
                    if ( Event->active.gain )    OnMouseFocus();
                    else                OnMouseBlur();
 
                    break;
                }
                case SDL_APPINPUTFOCUS: {
                    if ( Event->active.gain )    OnInputFocus();
                    else                OnInputBlur();
 
                    break;
                }
                case SDL_APPACTIVE:    {
                    if ( Event->active.gain )    OnRestore();
                    else                OnMinimize();
 
                    break;
                }
            }
            break;
        }
 
        case SDL_KEYDOWN: {
            OnKeyDown(Event->key.keysym.sym,Event->key.keysym.mod,Event->key.keysym.unicode);
            break;
        }
 
        case SDL_KEYUP: {
            OnKeyUp(Event->key.keysym.sym,Event->key.keysym.mod,Event->key.keysym.unicode);
            break;
        }
 
        case SDL_MOUSEMOTION: {
            OnMouseMove(Event->motion.x,Event->motion.y,Event->motion.xrel,Event->motion.yrel,(Event->motion.state&SDL_BUTTON(SDL_BUTTON_LEFT))!=0,(Event->motion.state&SDL_BUTTON(SDL_BUTTON_RIGHT))!=0,(Event->motion.state&SDL_BUTTON(SDL_BUTTON_MIDDLE))!=0);
            break;
        }
 
        case SDL_MOUSEBUTTONDOWN: {
            switch(Event->button.button) {
                case SDL_BUTTON_LEFT: {
                    OnLButtonDown(Event->button.x,Event->button.y);
                    break;
                }
                case SDL_BUTTON_RIGHT: {
                    OnRButtonDown(Event->button.x,Event->button.y);
                    break;
                }
                case SDL_BUTTON_MIDDLE: {
                    OnMButtonDown(Event->button.x,Event->button.y);
                    break;
                }
            }
            break;
        }
 
        case SDL_MOUSEBUTTONUP:    {
            switch(Event->button.button) {
                case SDL_BUTTON_LEFT: {
                    OnLButtonUp(Event->button.x,Event->button.y);
                    break;
                }
                case SDL_BUTTON_RIGHT: {
                    OnRButtonUp(Event->button.x,Event->button.y);
                    break;
                }
                case SDL_BUTTON_MIDDLE: {
                    OnMButtonUp(Event->button.x,Event->button.y);
                    break;
                }
            }
            break;
        }
 
        case SDL_JOYAXISMOTION: {
            OnJoyAxis(Event->jaxis.which,Event->jaxis.axis,Event->jaxis.value);
            break;
        }
 
        case SDL_JOYBALLMOTION: {
            OnJoyBall(Event->jball.which,Event->jball.ball,Event->jball.xrel,Event->jball.yrel);
            break;
        }
 
        case SDL_JOYHATMOTION: {
            OnJoyHat(Event->jhat.which,Event->jhat.hat,Event->jhat.value);
            break;
        }
        case SDL_JOYBUTTONDOWN: {
            OnJoyButtonDown(Event->jbutton.which,Event->jbutton.button);
            break;
        }
 
        case SDL_JOYBUTTONUP: {
            OnJoyButtonUp(Event->jbutton.which,Event->jbutton.button);
            break;
        }
 
        case SDL_QUIT: {
            OnExit();
            break;
        }
 
        case SDL_SYSWMEVENT: {
            //Ignore
            break;
        }
 
        case SDL_VIDEORESIZE: {
            OnResize(Event->resize.w,Event->resize.h);
            break;
        }
 
        case SDL_VIDEOEXPOSE: {
            OnExpose();
            break;
        }
 
        default: {
            OnUser(Event->user.type,Event->user.code,Event->user.data1,Event->user.data2);
            break;
        }
    }
}
 
void CEvent::OnInputFocus() {
    //Pure virtual, do nothing
}
 
void CEvent::OnInputBlur() {
    //Pure virtual, do nothing
}
 
void CEvent::OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode) {
    //Pure virtual, do nothing
}
 
void CEvent::OnKeyUp(SDLKey sym, SDLMod mod, Uint16 unicode) {
    //Pure virtual, do nothing
}
 
void CEvent::OnMouseFocus() {
    //Pure virtual, do nothing
}
 
void CEvent::OnMouseBlur() {
    //Pure virtual, do nothing
}
 
void CEvent::OnMouseMove(int mX, int mY, int relX, int relY, bool Left,bool Right,bool Middle) {
    //Pure virtual, do nothing
}
 
void CEvent::OnMouseWheel(bool Up, bool Down) {
    //Pure virtual, do nothing
}
 
void CEvent::OnLButtonDown(int mX, int mY) {
    //Pure virtual, do nothing
}
 
void CEvent::OnLButtonUp(int mX, int mY) {
    //Pure virtual, do nothing
}
 
void CEvent::OnRButtonDown(int mX, int mY) {
    //Pure virtual, do nothing
}
 
void CEvent::OnRButtonUp(int mX, int mY) {
    //Pure virtual, do nothing
}
 
void CEvent::OnMButtonDown(int mX, int mY) {
    //Pure virtual, do nothing
}
 
void CEvent::OnMButtonUp(int mX, int mY) {
    //Pure virtual, do nothing
}
 
void CEvent::OnJoyAxis(Uint8 which,Uint8 axis,Sint16 value) {
    //Pure virtual, do nothing
}
 
void CEvent::OnJoyButtonDown(Uint8 which,Uint8 button) {
    //Pure virtual, do nothing
}
 
void CEvent::OnJoyButtonUp(Uint8 which,Uint8 button) {
    //Pure virtual, do nothing
}
 
void CEvent::OnJoyHat(Uint8 which,Uint8 hat,Uint8 value) {
    //Pure virtual, do nothing
}
 
void CEvent::OnJoyBall(Uint8 which,Uint8 ball,Sint16 xrel,Sint16 yrel) {
    //Pure virtual, do nothing
}
 
void CEvent::OnMinimize() {
    //Pure virtual, do nothing
}
 
void CEvent::OnRestore() {
    //Pure virtual, do nothing
}
 
void CEvent::OnResize(int w,int h) {
    //Pure virtual, do nothing
}
 
void CEvent::OnExpose() {
    //Pure virtual, do nothing
}
 
void CEvent::OnExit() {
    //Pure virtual, do nothing
}
 
void CEvent::OnUser(Uint8 type, int code, void* data1, void* data2) {
    //Pure virtual, do nothing
}


Да… Очень много кода, но все SDL события должны быть покрыты (т.е. как-то обрабатываться). В этом обработчике, мы принимаем указатель на событие типа SDL_Event, а затем, в зависимости от вида события (нажатие клавиши или перемещение мыши) вызываем соответствующую функцию. Не пугайтесь такого объема кода, на самом деле тут всё предельно просто.
Теперь, когда у нас всё настроено, давайте перейдем в CApp.h и добавим созданный класс:

CApp.h
#ifndef _CAPP_H_
    #define _CAPP_H_
 
#include <SDL.h>
 
#include "CEvent.h"
#include "CSurface.h"
 
class CApp : public CEvent {
    private:
        bool            Running;
 
        SDL_Surface*    Surf_Display;
 
        SDL_Surface*    Surf_Test;
 
    public:
        CApp();
 
        int OnExecute();
 
    public:
        bool OnInit();
 
        void OnEvent(SDL_Event* Event);
 
        void OnLoop();
 
        void OnRender();
 
        void OnCleanup();
};
 
#endif


Все должно нормально откомпилироваться. У нас есть настроенный класс обработки событий, осталось связать его с основным классом игры. Откройте CApp_OnEvent.cpp и редактировать следующие функции:

CApp_OnEvent.cpp
#include "CApp.h"
 
void CApp::OnEvent(SDL_Event* Event) {
    CEvent::OnEvent(Event);
}


Теперь наше сообщение будет передаваться в класс и там корректно обрабатываться. Наша функция проверки события теперь переопределена. Мы избавились от проверки на SDL_Quit, и вместо этого передаем событие во внутреннюю функцию. Снова откройте CApp.h снова, и добавить следующие функции:

CApp.h
#ifndef _CAPP_H_
    #define _CAPP_H_
 
#include <SDL.h>
 
#include "CEvent.h"
#include "CSurface.h"
 
class CApp : public CEvent {
    private:
        bool            Running;
 
        SDL_Surface*    Surf_Display;
 
        SDL_Surface*    Surf_Test;
 
    public:
        CApp();
 
        int OnExecute();
 
    public:
        bool OnInit();
 
        void OnEvent(SDL_Event* Event);
 
        void OnExit(); 
 
        void OnLoop();
 
        void OnRender();
 
        void OnCleanup();
};
 
#endif


Функция OnExit будет обрабатывать событие SDL_Quit (нажатие пользователем крестика). Итак, прототип у нас есть, осталось запрограммировать его функционал. Откройте CApp_OnEvent.cpp, и добавьте следующее:

CApp_OnEvent.cpp
#include "CApp.h"
 
void CApp::OnEvent(SDL_Event* Event) {
    CEvent::OnEvent(Event);
}
 
void CApp::OnExit() {
    Running = false;
}


Перекомпилируйте, и запустите. Вы можете закрыть приложение, совсем как и раньше.
Я рекомендую вам ознакомиться с другими видами событий событий, попробовать прописать ответ на них в соответствующих функциях-обработчиках, потому-что в дальнейшем мы будем использовать некоторые из этих событий в наших играх.
В следующем уроке нас ждет увлекательное путешествие, в конце которого мы создадим нашу первую реальную игру — Tic-Tac-Toe (Крестики/Нолики).
Какой-то маленький получился урок, несмотря на большой объем кода. Видимо придется мне дополнить его небольшим примером обработки клавиш UP, DOWN, RIGHT и LEFT (стрелочки).

Откройте CEvent.cpp, найдите там функцию CEvent::OnKeyDown и напишите в ней следующее:

CEvent.cpp
В самом начале файла добавьте
#include <iostream>
using namespace std;

А потом перепишите функционал CEvent::OnKeyDown
void CEvent::OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode) {
    switch (sym)
    {
        case SDLK_ESCAPE:
        {
            cout << "Hey, HABR! Escape pressed by m0sk1t\n";
            break;
        }
        case SDLK_UP:
        {
            cout << "Hey, HABR! UP pressed by m0sk1t\n";
            break;
        }
        case SDLK_DOWN:
        {
            cout << "Hey, HABR! DOWN pressed by m0sk1t\n";
            break;
        }
        case SDLK_LEFT:
        {
            cout << "Hey, HABR! LEFT pressed by m0sk1t\n";
            break;
        }
        case SDLK_RIGHT:
        {
            cout << "Hey, HABR! RIGHT pressed by m0sk1t\n";
            break;
        }
        default: break;
    }
}


Откомпилируйте и запустите, а затем понажимайте стрелочки и закройте окно игры. Вы должны наблюдать примерно это:


Видите консольный вывод? Он отображает результат того что в функции CEvent::OnKeyDown мы теперь отслеживаем факт нажатия одной из стрелочек и выводим описание того какая из них была нажата (ну и клавиша Escape туда попала).

Ссылки на исходный код:


Ссылки на все уроки:
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+8
Comments 0
Comments Leave a comment

Articles