Let's start with the event handler. Here's a simple class for dealing with SDL events (SdlEventQueue.h -- BitBucket):
class SdlEventQueue : public EventQueue { public: SdlEventQueue() { } virtual ~SdlEventQueue() { } virtual void pollEvents(); void addEventCallback(SdlEventCallbackPtr& callback); private: std::map<SDL_EventType, SdlEventCallbackPtr>callbacks_; };
(It derives from a generic EventQueue class, but I don't know how much use that's going to be in the end.)
This leads to a pleasantly simple implementation (SdlEventQueue.cc -- BitBucket):
void SdlEventQueue::pollEvents() { SDL_Event event; while(SDL_PollEvent(&event)) { const SDL_EventType type = static_cast<SDL_EventType>(event.type); const SdlEventCallbackPtr callback = callbacks_[type]; if(callback) { callback->fire(event); } } } void SdlEventQueue::addEventCallback(SdlEventCallbackPtr& callback) { const SDL_EventType type = callback->type(); if(callbacks_[type]) { callbacks_[type]->chain(callback); } else { callbacks_[type] = callback; } }
If it weren't for the SDL types involved, this could be fully generic. It's much nicer than what I had before: No need for a big long switch statement, no event-specific logic in the queue wrapper; hell, the queue doesn't even care whether there's anything on the other end of the event callbacks.
Speaking of which: (SdlEventCallback.h -- BitBucket)
class SdlEventCallback { public: SdlEventCallback() : next_() { } virtual ~SdlEventCallback() { } virtual void chain(SdlEventCallbackPtr& next) { next_ = next; } virtual SDL_EventType type() = 0; virtual void fire(const SDL_Event& event) = 0; protected: virtual void chainFire(const SDL_Event& event) { if(next_) next->fire(event); } private: SdlEventCallbackPtr next_;
The only really interesting thing going on here is the chaining mechanism, which is a pretty simple implementation of the Chain of Responsibility pattern. I can imagine having four gamepads hooked up, and a chain of joystick event handlers passing along input events to whichever callback object needs to handle them. For the most part, though, I don't expect chaining to happen.
This is an abstract base class, though, and so far I've implemented only two event callbacks (SdlKeyboardCallbacks.h and SdlKeyboardCallbacks.cc -- BitBucket). Here's the KeyDown callback:
// In SdlKeyboardCallbacks.h class SdlKeyDownCallback : public SdlEventCallback { public: SdlKeyDownCallback(KeyboardPtr& keyboard) : keyboard_(keyboard) { } virtual SDL_EventType type() { return SDL_KEYDOWN; } virtual void fire(const SDL_Event& event); private: KeyboardPtr keyboard_; }; // In SdlKeyboardCallbacks.cc void SdlKeyDownCallback::fire(const SDL_Event& event) { const InputKey key = scancode_crosstab_[event.key.keysym.scancode]; keyboard_->setKeyDown(key, true); }
Nice and simple. It turns out that only the keyboard callbacks need to know (or care) about the massive SDL-to-InputKey translation table, so I've stuck that into an anonymous namespace in SdlKeyboardCallbacks.cc.
Keyboards have turned out to be pretty simple creatures too (Keyboard.h -- BitBucket):
lass Keyboard : public Controller { public: Keyboard() { for(int k = 0; k < KEY_MAX_ELEMS; k++) { key_down_[k] = false; } } virtual ~Keyboard() { } bool keyDown(InputKey key) const { return key_down_[key]; } void setKeyDown(InputKey key, bool val) { key_down_[key] = val; } const char* keyName(InputKey key) const { return key_names_[key]; } protected: private: bool key_down_[KEY_MAX_ELEMS]; static const char* key_names_[KEY_MAX_ELEMS]; };
(keyName can probably be a static function, there.) The only real concern I have about this class is that setKeyDown is part of the public interface -- it's there for the keyboard callbacks, but I'm a bit hesitant about Keyboard just handing any client the opportunity to change its internal state. I don't want to make it private and friend the keyboard callbacks, because right now Keyboard doesn't even have to know that callbacks exist. For now I'm just going to leave it as-is.
The only part of the input-handling process that got any more complex -- besides the architecture, at least -- is the allocation in SdlAppFramework:
KeyboardPtr SdlAppFramework::keyboard() { if(keyboard_ == NULL) { keyboard_ = KeyboardPtr(new Keyboard); SdlEventCallbackPtr down(new SdlKeyDownCallback(keyboard_)); events_->addEventCallback(down); SdlEventCallbackPtr up(new SdlKeyUpCallback(keyboard_)); events_->addEventCallback(up); } return keyboard_; }
Here we create callbacks at the same time as the Keyboard object, because they need to know about the keyboard when they're constructed (and the Keyboard is pretty useless without them). It still isn't all that big a deal.
I've made some other minor changes to the code, moving the event-polling interface from Input to AppFramework and otherwise shifting things around.
Enough with the toolkit wrapper! Next on my list is dealing with OpenGL shaders so I can start drawing stuff. That means I'm going to have to decide on a linear algebra library pretty soon. I think I'll go with Bullet as a general-purpose toolkit, and use its built-in vector/matrix stuff.
No comments:
Post a Comment