Translation of the SDL Game Framework Series. Part 3 - SDL Events

Original author: Tim Jones
  • Transfer
  • Tutorial
One of the foundations of game development is the processing of messages arriving during any Events . All video games, from tennis to very complex games for PCs and consoles, use the so-called. events for interacting with the player. These events can come from keyboards, mice, joysticks, gamepads, etc., as well as from the operating system itself. It is important to understand how they work if we want to properly handle user interaction with the game. We have already used events, but only to close our window, now we will go deeper into how to receive events from the user.

As you already noticed, each lesson is built on the basis of the previous one, so we will not deviate from this tradition for now. In order to track all events and process them in the functions intended for this, you need to create a new class. Add the two files CEvent.h and CEvent.cpp to the project . In these two files we will process incoming messages and call the corresponding function. Our CApp class will inherit from this class, so when we start processing events, we will simply override its functions.

Open CEvent.h and add the following code:

CEvent.h
#ifndef _CEVENT_H_
    #define _CEVENT_H_
#include 
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


The sickly turned out a class? Well, now open CEvent.cpp , and add the following code:

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
}


Yes ... A lot of code, but all SDL events should be covered (i.e. somehow handled). In this handler, we accept a pointer to an event of type SDL_Event , and then, depending on the type of event (pressing a key or moving the mouse), we call the corresponding function. Do not be alarmed by this amount of code, in fact, everything is extremely simple here.
Now that everything is set up for us, let's go to CApp.h and add the created class:

CApp.h
#ifndef _CAPP_H_
    #define _CAPP_H_
#include 
#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


Everything should compile normally. We have a customized event handling class, it remains to associate it with the main class of the game. Open CApp_OnEvent.cpp and edit the following functions:

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


Now our message will be passed to the class and processed correctly there. Our event validation function is now overridden. We got rid of the SDL_Quit check , and instead pass the event to an internal function. Open CApp.h again, and add the following functions:

CApp.h
#ifndef _CAPP_H_
    #define _CAPP_H_
#include 
#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


The OnExit function will handle the SDL_Quit event (clicking the cross). So, we have a prototype, it remains to program its functionality. Open CApp_OnEvent.cpp , and add the following:

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


Recompile and run. You can close the application, just like before.
I recommend that you familiarize yourself with other types of event events, try to register the answer to them in the corresponding handler functions, because in the future we will use some of these events in our games.
In the next lesson, an exciting journey awaits us, at the end of which we will create our first real game - Tic-Tac-Toe (Tic Tac Toe).
Some little lesson turned out, despite the large amount of code. Apparently I have to supplement it with a small example of processing the keys UP , DOWN , RIGHT and LEFT (arrows).

Open CEvent.cpp, find the CEvent :: OnKeyDown function there and write the following in it:

CEvent.cpp
At the very beginning of the file, add
#include 
using namespace std;

And then rewrite the functionality of 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;
    }
}


Compile and run, and then click the arrows and close the game window. You should observe something like this:


See console output? It displays the result of the fact that in the function CEvent :: OnKeyDown we now track the fact of clicking one of the arrows and display a description of which one was pressed (well, the Escape key got there).

Source Code Links:


Links to all lessons:

Also popular now: