Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Mastering SFML Game Development

You're reading from   Mastering SFML Game Development Inject new life and light into your old SFML projects by advancing to the next level.

Arrow left icon
Product type Paperback
Published in Jan 2017
Publisher Packt
ISBN-13 9781786469885
Length 442 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Raimondas Pupius Raimondas Pupius
Author Profile Icon Raimondas Pupius
Raimondas Pupius
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

Preface 1. Under the Hood - Setting up the Backend FREE CHAPTER 2. Its Game Time! - Designing the Project 3. Make It Rain! - Building a Particle System 4. Have Thy Gear Ready - Building Game Tools 5. Filling the Tool Belt - a few More Gadgets 6. Adding Some Finishing Touches - Using Shaders 7. One Step Forward, One Level Down - OpenGL Basics 8. Let There Be Light - An Introduction to Advanced Lighting 9. The Speed of Dark - Lighting and Shadows 10. A Chapter You Shouldnt Skip - Final Optimizations

Managing application events

Event management is one of the cornerstones that provide us with fluid control experience. Any key presses, window changes, or even custom events created by the GUI system we'll be covering later are going to be processed and handled by this system. In order to effectively unify event information coming from different sources, we first must unify their types by enumerating them correctly:

enum class EventType { 
  KeyDown = sf::Event::KeyPressed, 
  KeyUp = sf::Event::KeyReleased, 
  MButtonDown = sf::Event::MouseButtonPressed, 
  MButtonUp = sf::Event::MouseButtonReleased, 
  MouseWheel = sf::Event::MouseWheelMoved, 
  WindowResized = sf::Event::Resized, 
  GainedFocus = sf::Event::GainedFocus, 
  LostFocus = sf::Event::LostFocus, 
  MouseEntered = sf::Event::MouseEntered, 
  MouseLeft = sf::Event::MouseLeft, 
  Closed = sf::Event::Closed, 
  TextEntered = sf::Event::TextEntered, 
  Keyboard = sf::Event::Count + 1, Mouse, Joystick, 
  GUI_Click, GUI_Release, GUI_Hover, GUI_Leave 
}; 
 
enum class EventInfoType { Normal, GUI }; 

SFML events come first, since they are the only ones following a strict enumeration scheme. They are then followed by the live SFML input types and four GUI events. We also enumerate event information types, which are going to be used inside this structure:

struct EventInfo { 
  EventInfo() : m_type(EventInfoType::Normal), m_code(0) {} 
  EventInfo(int l_event) : m_type(EventInfoType::Normal), 
    m_code(l_event) {} 
  EventInfo(const GUI_Event& l_guiEvent): 
    m_type(EventInfoType::GUI), m_gui(l_guiEvent) {} 
  EventInfo(const EventInfoType& l_type) { 
    if (m_type == EventInfoType::GUI) { DestroyGUIStrings(); } 
    m_type = l_type; 
    if (m_type == EventInfoType::GUI){ CreateGUIStrings("", ""); } 
  } 
 
  EventInfo(const EventInfo& l_rhs) { Move(l_rhs); } 
 
  EventInfo& operator=(const EventInfo& l_rhs) { 
    if (&l_rhs != this) { Move(l_rhs); } 
    return *this; 
  } 
 
  ~EventInfo() { 
    if (m_type == EventInfoType::GUI) { DestroyGUIStrings(); } 
  } 
  union { 
    int m_code; 
    GUI_Event m_gui; 
  }; 
   
  EventInfoType m_type; 
private: 
  void Move(const EventInfo& l_rhs) { 
    if (m_type == EventInfoType::GUI) { DestroyGUIStrings(); } 
    m_type = l_rhs.m_type; 
    if (m_type == EventInfoType::Normal){ m_code = l_rhs.m_code; } 
    else { 
      CreateGUIStrings(l_rhs.m_gui.m_interface, 
        l_rhs.m_gui.m_element); 
      m_gui = l_rhs.m_gui; 
    } 
  } 
 
  void DestroyGUIStrings() { 
    m_gui.m_interface.~basic_string(); 
    m_gui.m_element.~basic_string(); 
  } 
 
  void CreateGUIStrings(const std::string& l_interface, 
    const std::string& l_element) 
  { 
    new (&m_gui.m_interface) std::string(l_interface); 
    new (&m_gui.m_element) std::string(l_element); 
  } 
}; 

Because we care about more than just the event type that took place, there needs to be a good way of storing additional data that comes with it. C++11's unrestricted union is a perfect candidate for that. The only downside is that now we have to worry about manually managing the data inside the union, which comes complete with data allocations and direct invocation of destructors.

As event call-backs are being invoked, it's a good idea to provide them with the actual event information. Because it's possible to construct more complex requirements for specific call-backs, we can't get away with unions this time. Any possible information that may be relevant needs to be stored, and that's precisely what is done here:

struct EventDetails { 
  EventDetails(const std::string& l_bindName): m_name(l_bindName){ 
    Clear(); 
  } 
   
  std::string m_name; 
  sf::Vector2i m_size; 
  sf::Uint32 m_textEntered; 
  sf::Vector2i m_mouse; 
  int m_mouseWheelDelta; 
  int m_keyCode; // Single key code. 
 
  std::string m_guiInterface; 
  std::string m_guiElement; 
  GUI_EventType m_guiEvent; 
 
  void Clear() { ... } 
}; 

This structure is filled with every single bit of information that is available as the events are processed, and then passed as an argument to the call-back that gets invoked. It also provides a Clear() method, because instead of being created only for the time during the call-back, it lives inside the binding structure:

using Events = std::vector<std::pair<EventType, EventInfo>>; 
 
struct Binding { 
  Binding(const std::string& l_name) : m_name(l_name), 
    m_details(l_name), c(0) {} 
  void BindEvent(EventType l_type, EventInfo l_info = EventInfo()) 
  { ... } 
 
  Events m_events; 
  std::string m_name; 
  int c; // Count of events that are "happening". 
 
  EventDetails m_details; 
}; 

A binding is what actually allows events to be grouped together in order to form more complex requirements. Think of it in terms of multiple keys needing to be pressed at once in order to perform an action, such as Ctrl + C for copying a piece of text. A binding for that type of situation would have two events it's waiting for: the Ctrl key and the C key.

Event manager interface

With all of the key pieces being covered, all that's left is actually managing everything properly. Let's start with some type definitions:

using Bindings = std::unordered_map<std::string, 
  std::unique_ptr<Binding>>; 
using CallbackContainer = std::unordered_map<std::string, 
  std::function<void(EventDetails*)>>; 
enum class StateType; 
using Callbacks = std::unordered_map<StateType, 
  CallbackContainer>; 

All bindings are attached to specific names that get loaded from a keys.cfg file when the application is started. It follows a basic format like this:

Window_close 0:0 
Fullscreen_toggle 5:89 
Intro_Continue 5:57 
Mouse_Left 9:0 

Of course these are very basic examples. More complex bindings would have multiple events separated by white spaces.

Call-backs are also stored in an unordered map, as well as tied to the name of a binding that they're watching. The actual call-back containers are then grouped by state, in order to avoid multiple functions/methods getting called when similar keys are pressed. As you can imagine, the event manager is going to be inheriting from a StateDependent class for this very reason:

class EventManager : public StateDependent{ 
public: 
  ... 
  bool AddBinding(std::unique_ptr<Binding> l_binding); 
  bool RemoveBinding(std::string l_name); 
  void ChangeState(const StateType& l_state); 
  void RemoveState(const StateType& l_state); 
  void SetFocus(bool l_focus); 
   
  template<class T> 
  bool AddCallback(const StateType& l_state, 
    const std::string& l_name,  
    void(T::*l_func)(EventDetails*), T* l_instance) 
  { ... } 
   
  template<class T> 
  bool AddCallback(const std::string& l_name, 
    void(T::*l_func)(EventDetails*), T* l_instance) 
  { ... } 
 
  bool RemoveCallback(const StateType& l_state, 
    const std::string& l_name){ ... } 
  void HandleEvent(sf::Event& l_event); 
  void HandleEvent(GUI_Event& l_event); 
  void Update(); 
  sf::Vector2i GetMousePos(sf::RenderWindow* l_wind = nullptr) 
    const { ... } 
private: 
  ... 
  Bindings m_bindings; 
  Callbacks m_callbacks; 
}; 

Once again, this is quite simple. Since this is a state-dependent class, it needs to implement the ChangeState() and RemoveState() methods. It also keeps track of when the window focus is obtained/lost, in order to avoid polling events of minimized/unfocused windows. Two versions of AddCallback are provided: one for a specified state, and one for the current state. Separate HandleEvent() methods are also available for every event type supported. So far, we only have two: SFML events, and GUI events. The latter is going to be used in the upcoming section.

You have been reading a chapter from
Mastering SFML Game Development
Published in: Jan 2017
Publisher: Packt
ISBN-13: 9781786469885
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime