I've written a Controller for the keyboard so that I can map certain keys to callbacks within a game. A key can only call one callback, but a callback can be called by many keys.
Currently, a Controller has two std::map
s - one for when the key(s) is pressed/held, and one for when the key(s) are released. These maps are SDL_Scancode
to std::string
(Which I may change later to an enum rather than a string? But strings are easier to read so maybe not.) The Controller also has a ControllerContext
object, which is just a wrapper around two std::map
s of std::string
to std::function<void(void)>
: one map for callbacks to happen when a key is pressed, another for callbacks to happen when a key is released.
I feel like some kind of change could be made to make the code more readable, and I am unsure of if I should have the context stored in a shared_ptr
, or if I should just keep a raw pointer within the Controller.
This is how it would work:
//An event union provided by SDL
//https://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlevent.html
SDL_Event e;
//A controller
Controller controller_;
ControllerContext context_;
//Build the context
controller_.SetContext(&context);
//Poll all events in the event queue, feeding them to the structure e
while(SDL_PollEvent(&e))
{
switch(e.type)
case SDL_KEYDOWN:
controller_.OnKeyPress(e.key);
break;
case SDL_KEYUP:
controller_.OnKeyRelease(e.key);
break;
default:
break;
}
keyboardcontroller.h
#ifndef SHMUPPY_HEADER_KEYBOARDCONTROLLER_H_
#define SHMUPPY_HEADER_KEYBOARDCONTROLLER_H_
#include <map>
#include <SDL.h>
#include <iostream>
#include <exception>
#include <string>
#include <memory>
struct ControllerContext;
class Controller
{
public:
Controller();
int OnKeyPress(const SDL_Event &e);
int OnKeyRelease(const SDL_Event &e);
void SetContext(std::shared_ptr<ControllerContext> context_);
protected:
std::map<SDL_Scancode, std::string> keypress_action_map_;
std::map<SDL_Scancode, std::string> keyrelease_action_map_;
std::shared_ptr<ControllerContext> controller_context_;
};
#endif
keyboardcontroller.cpp
#include "KeyboardController.h"
#include "ControllerContext.h"
Controller::Controller()
{
//Some test initializations
keypress_action_map_[SDL_SCANCODE_LEFT] = "Left";
keypress_action_map_[SDL_SCANCODE_DOWN] = "Down";
keypress_action_map_[SDL_SCANCODE_RIGHT] = "Right";
keypress_action_map_[SDL_SCANCODE_UP] = "Up";
keyrelease_action_map_[SDL_SCANCODE_LEFT] = "Left";
keyrelease_action_map_[SDL_SCANCODE_DOWN] = "Down";
keyrelease_action_map_[SDL_SCANCODE_RIGHT] = "Right";
keyrelease_action_map_[SDL_SCANCODE_UP] = "Up";
}
int Controller::OnKeyPress(const SDL_Event& e)
{
try
{
//std::cout << keypress_action_map_.at(e.key.keysym.scancode) << std::endl;
controller_context_->keypress_events_.at(keypress_action_map_.at(e.key.keysym.scancode))();
return 1;
}
catch (std::exception ex)
{
return 0;
}
}
int Controller::OnKeyRelease(const SDL_Event& e)
{
try
{
std::cout << keyrelease_action_map_.at(e.key.keysym.scancode) << std::endl;
return 1;
}
catch (std::exception ex)
{
return 0;
}
}
void Controller::SetContext(std::shared_ptr<ControllerContext> context_)
{
controller_context_ = context_;
}
ControllerContext.h
#ifndef SHMUPPY_HEADER_CONTROLLER_CONTEXT_H
#define SHMUPPY_HEADER_CONTROLLER_CONTEXT_H
#include <map>
#include <functional>
struct ControllerContext
{
std::map<std::string, std::function<void(void)>> keypress_events_;
std::map<std::string, std::function<void(void)>> keyrelease_events_;
};
#endif