for my most recent thing I'm transferring the sdl event to an alternate class specifically designed to handle it. I then store pointers to functions for each of the buttons to be executed when sdl detects a keydown or keyup event.
Unfortunately when passing pointers to functions (if the function is a function of a class) the function has to be static, so I also pass a pointer to the object of the class that the keypress handling class will call the function of.
it boils down to this:
game/engine base class (Base.h):
#include "InputHandler.h"
class Base
{
public:
Base()
{
}
virtual ~Base() {}
static void CallBackFunc(int button,int typeCB, void* cla)
{
Base* BASEP = static_cast<Base*>(cla);
switch(typeCB)
{
case MOUSE_DOWN:
BASEP->MouseDown(button);
break;
case MOUSE_UP:
BASEP->MouseUp(button);
break;
case BUTTON_DOWN:
BASEP->ButtonDown(button);
break;
case BUTTON_UP:
BASEP->ButtonUp(button);
break;
}
}
void ButtonDown(int button)
{
std::cout<<"Button "<<button<<" down\n";
}
void ButtonUp(int button)
{
std::cout<<"Button "<<button<<" up\n\n";
}
void MouseDown(int button)
{
std::cout<<"Mouse "<<button<<" down\n";
}
void MouseUp(int button)
{
std::cout<<"Mouse "<<button<<" up\n\n";
}
void Initialise()
{
INHANDLER.RegisterCallback(&CallBackFunc,this);//this is the pointer to itself which is passed to the input handling class
}
InputHandler INHANDLER;
protected:
private:
};
This has a few important things:
-A callback function which the input handler will call on an sdl event (eg, SDL_KEYDOWN)
-specialised functions for each of the possible sdl events
-An instance of the InputHandler class (below)
-An initialisation functions which registers the callback function and the pointer to the class
input handling class (in InputHandler.h):
enum
{
MOUSE_DOWN,
MOUSE_UP,
BUTTON_DOWN,
BUTTON_UP
};
class InputHandler
{
public:
InputHandler() {}
virtual ~InputHandler() {}
void PassEventHandler(SDL_Event ev)
{
SDL_GetMouseState(&x,&y);
while (SDL_PollEvent(&ev))
{
switch (ev.type)
{
case SDL_QUIT:
eFunc();
break;
case SDL_KEYDOWN:
if (ev.key.keysym.sym == SDLK_ESCAPE)
{
eFunc();
}
else
{
Keys[ev.key.keysym.sym] = true;
cbFunc(ev.key.keysym.sym,BUTTON_DOWN,ClassPointer);
}
break;
case SDL_KEYUP:
Keys[ev.key.keysym.sym] = false;
cbFunc(ev.key.keysym.sym,BUTTON_UP,ClassPointer);
break;
case SDL_MOUSEBUTTONDOWN:
if (ev.button.button == SDL_BUTTON_LEFT)
{
cbFunc(1,MOUSE_DOWN,ClassPointer);
}
if (ev.button.button == SDL_BUTTON_RIGHT)
{
cbFunc(2,MOUSE_DOWN,ClassPointer);
}
break;
case SDL_MOUSEBUTTONUP:
if (ev.button.button == SDL_BUTTON_LEFT)
{
cbFunc(1,MOUSE_UP,ClassPointer);
}
if (ev.button.button == SDL_BUTTON_RIGHT)
{
cbFunc(2,MOUSE_UP,ClassPointer);
}
break;
case SDL_MOUSEMOTION:
x = ev.button.x;
y = ev.button.y;
break;
}
}
}
int GetWinMousePosX()
{
return x;
}
int GetWinMousePosY()
{
return y;
}
bool ButtonDown(int Button)
{
return Keys[Button];
}
int RegisterExitCallback(void (*pt2Func)(void))
{
if (pt2Func == NULL)
{
return 1;
}
eFunc = pt2Func;
return 0;
}
int RegisterCallback(void (*pt2Func)(int,int,void*),void* pt2Class)
{
if (pt2Class != NULL)
{
ClassPointer = pt2Class;
}
else
{
return 1;
}
if (pt2Func != NULL)
{
cbFunc = pt2Func;
}
else
{
return 2;
}
return 0;
}
protected:
private:
bool Keys[322];
bool left, right;
int x,y;
void (*cbFunc)(int,int,void*);
void (*eFunc)(void);
void* ClassPointer;
};
The input handler class basically holds a couple of things:
-The pointer to the class:
this is passed to the static callback function so it can access other properties of the class (static functions can only access other static functions and variables)
-The call back function pointer:
This is called every time a SDL_KEYDOWN or SDL_KEYUP even is found
-The exit function pointer:
This is called if the input class finds a SDLK_ESCAPE in the SDL_KEYDOWN case, and basically quits the sdl while loop.
-The buttons array, mouse buttons variables and x/y positions variables:
This is here to store a persistent record of which buttons are down and which buttons (or mouse buttons) are up so that a function can access that property without waiting for a SDL_KEYDOWN or KEYUP case. The x/y properties are used to store the WINDOWS mouse coordinates, not the coordinates in the opengl scene.
-A set of functions to easily return the property of a button or the location of the mouse pointer
to use this you just need a couple of simple things in you sdl while loop (heres my one):
GAME.Initialise();
GAME.INHANDLER.RegisterExitCallback(&ExitNicely);
while (!done)
{
SDL_Event ev; //Create the SDL_Event object to receive and process keyboard and mouse input
GAME.INHANDLER.PassEventHandler(ev);
GAME.Render();
}
Here I initialise the instance of my base class (named GAME) and registering the exit function; ExitNicely, which I have created earlier and which basically sets done (the boolean value in the loop) to true. Then, while it is looping I first get the SDL_Event and pass it to my input handler in the base class which in turn will call any functions registered with it. I then render the game and loop again.
This might seem like a pretty pointlessly complicated system but it allows for three main things:
- The input handler can be passed to any function or class, effectively giving that class (or function) the ability to get information about button or mouse states
- It can be used in any class. As the pointer to the class is a void pointer, any pointer can be used here and extracted
- The callback function, what it does and the functions it in turn calls (or doesnt call) can be defined in the class using the input handler class, instead of in the input handler, meaning it can be used within multiple different base classes
Anyway, that's just how I do it, you might want to look into using the sdl polling loop within a different class or function, but you might not want to go all the way into using function pointers.
also, to awesome c++ coders everywhere, some feedback on how
I do it would be great
[/list]