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

от автора

Одной из основ разработки игр является обработка сообщений поступающих во время каких-либо событий (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 туда попала).

Ссылки на все уроки:

ссылка на оригинал статьи http://habrahabr.ru/post/167245/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *