Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411276 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 12:04:52 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityTownhallForum IssuesArchived subforums (read only)TutorialsGame writing tips & design patterns (C++)
Pages: [1]
Print
Author Topic: Game writing tips & design patterns (C++)  (Read 3631 times)
soryy708
Level 3
***


View Profile WWW
« on: May 12, 2013, 05:12:05 AM »

Hello.

I write games in C++, using SDL and OpenGL (mostly). Here I will update and maintain a list of snippets that you might find useful. I post this in the tutorial section because you could learn from it.

I'll begin with an SDL Event handling system.
events.h:
Code:
#ifndef EVENTS_H_INCLUDED
#define EVENTS_H_INCLUDED

#include "SDL.h"

class EventHandler
{
public:
    virtual void handle(const SDL_Event sdl_event) = 0;

    EventHandler();
    ~EventHandler();
};

void updateEvents();

#endif

events.cpp:
Code:
#include <deque>
#include "events.h"

std::deque<EventHandler*> handlers;

EventHandler::EventHandler()
{
    handlers.push_back(this);
}

EventHandler::~EventHandler()
{
    bool found = false;
    for(unsigned int i = 0; i < handlers.size() && !(found); i++)
    {
        if(handlers[i] == this)
        {
            found = true;
            handlers[i] = handlers[handlers.size() - 1];
            handlers.pop_back();
        }
    }
}

void updateEvents()
{
    SDL_Event sdl_event;
    while(SDL_PollEvent(&sdl_event) > 0)
    {
        for(unsigned int i = 0; i < handlers.size(); i++)
        {
            handlers[i]->handle(sdl_event);
        }
    }
}

This is basically a listener that registers once it's instantiated. Read more: http://en.wikipedia.org/wiki/Observer_pattern

Now, let's say you want to use the event handling system I showed above.
Let's write a mouse class!
mouse.h:
Code:
#ifndef MOUSE_H_INCLUDED
#define MOUSE_H_INCLUDED

class MouseEventHandler;

class Mouse
{
public:
    friend MouseEventHandler;

    enum ButtonState
    {
        BUTTONSTATE_UP,
        BUTTONSTATE_DOWN
    };

private:
    ButtonState  left;
    ButtonState  middle;
    ButtonState  right;
    unsigned int x;
    unsigned int y;
    signed int   offx;
    signed int   offy;

public:
    bool         isLeftButtonDown()   const { return left   == BUTTONSTATE_DOWN; }
    bool         isMiddleButtonDown() const { return middle == BUTTONSTATE_DOWN; }
    bool         isRightButtonDown()  const { return right  == BUTTONSTATE_DOWN; }
    bool         isLeftButtonUp()     const { return left   == BUTTONSTATE_UP; }
    bool         isMiddleButtonUp()   const { return middle == BUTTONSTATE_UP; }
    bool         isRightButtonUp()    const { return right  == BUTTONSTATE_UP; }
    unsigned int getX()               const { return x; }
    unsigned int getY()               const { return y; }
    signed int   getXOffset()         const { return offx; }
    signed int   getYOffset()         const { return offy; }

    Mouse();
};

extern Mouse mouse;

#endif
mouse.cpp:
Code:
#include "mouse.h"
#include "events.h"

Mouse mouse;

class MouseEventHandler : public EventHandler
{
public:
    void handle(const SDL_Event sdl_event)
    {
        switch(sdl_event.type)
        {
        case(SDL_MOUSEBUTTONDOWN):
            switch(sdl_event.button.button)
            {
            case(SDL_BUTTON_LEFT):
                mouse.left = Mouse::BUTTONSTATE_DOWN;
                break;
            case(SDL_BUTTON_MIDDLE):
                mouse.middle = Mouse::BUTTONSTATE_DOWN;
                break;
            case(SDL_BUTTON_RIGHT):
                mouse.right = Mouse::BUTTONSTATE_DOWN;
                break;
            }
            break;
        case(SDL_MOUSEBUTTONUP):
            switch(sdl_event.button.button)
            {
            case(SDL_BUTTON_LEFT):
                mouse.left = Mouse::BUTTONSTATE_UP;
                break;
            case(SDL_BUTTON_MIDDLE):
                mouse.middle = Mouse::BUTTONSTATE_UP;
                break;
            case(SDL_BUTTON_RIGHT):
                mouse.right = Mouse::BUTTONSTATE_UP;
                break;
            }
            break;
        case(SDL_MOUSEMOTION):
            mouse.x = sdl_event.motion.x;
            mouse.y = sdl_event.motion.y;
            mouse.offx = sdl_event.motion.xrel;
            mouse.offy = sdl_event.motion.yrel;
            break;
        }
    }
};

MouseEventHandler mouse_event_handler;

Mouse::Mouse() :
        left(BUTTONSTATE_UP),
        middle(BUTTONSTATE_UP),
        right(BUTTONSTATE_UP),
        x(0),
        y(0),
        offx(0),
        offy(0)
{
}

Please note that for the mouse to work, you need to use the mouse in the header. Don't instantiate your own. However, for unit testing, you could write your own handler & use that. I didn't make it a singleton just because of unit testing. More about singletons: http://en.wikipedia.org/wiki/Singleton_pattern

By the way, to see it in action:
main.cpp:
Code:
#include <exception>
#include "SDL.h"
#include "events.h"

#define GAME_NAME "Sample Game 1"

bool running = true;

class WindowEventHandler : public EventHandler
{
public:
    void handle(const SDL_Event sdl_event)
    {
        if(sdl_event.type == SDL_QUIT)
        {
            running = false;
        }
    }
};

int main(int argc, char* argv[])
{
    try
    {
        if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
        {
            throw(std::runtime_error(SDL_GetError()));
        }
        SDL_WM_SetCaption(GAME_NAME, NULL);
        if(SDL_SetVideoMode(852, 480, 0, SDL_OPENGL) == NULL)
        {
            throw(std::runtime_error(SDL_GetError()));
        }

        WindowEventHandler window_event_handler;
        while(running)
        {
            updateEvents();
        }

        SDL_Quit();
    }
    catch(std::runtime_error& error)
    {
        fprintf(stderr, "%s", error.what());
        return 1;
    }
    
    return 0;
}

More will come, so stay up to date!
« Last Edit: May 14, 2013, 08:41:02 PM by soryy708 » Logged
George Michaels
Level 0
***


I like big butts and I can not lie


View Profile
« Reply #1 on: May 16, 2013, 04:13:44 AM »

Just because I can, here's my event system in C++.

Code:
#ifndef F_EVENTS
#define F_EVENTS

namespace f_events {

// Types of events
enum event_type {
GLFW_INPUT = 0,
NETWORK_PACKET,

EVENT_COUNT
};

// Parent object of an event
struct event {
event* next;

event_type type;
bool read;

event(event_type t) {
this->next = NULL;

this->type = t;
this->read = false;
}

virtual ~event() { }
};

// Buffer of events of a given type
template <class T>
class buffer {
public:
event* stack;
event* top;
event* current;

buffer(event* e) {
this->stack = this->top = this->current = e;
}

T* next() {
current = stack;
if(current != NULL)
stack = stack->next;

return (T*) current;
}

T* get() {
if(current != NULL)
current->read = true;

return (T*) current;
}

void reset() {
stack = top;
}
};

// List of events
event** events = NULL; // Pull from here
event** events_stack = NULL; // Push to here

// Push an event on to the event
void push(event* e) {
// Create the events arrays if they don't exist
if(events == NULL) {
events = new event*[EVENT_COUNT];
events_stack = new event*[EVENT_COUNT];

for(unsigned int a = 0; a < EVENT_COUNT; a++)
events[a] = events_stack[a] = NULL;
}

// FIRST-IN-FIRST-OUT MOTHERFUCKER
if(events_stack[e->type] == NULL) {
events_stack[e->type] = e;
events[e->type] = e;

} else {
events_stack[e->type]->next = e;
events_stack[e->type] = e;
}
}

// Clear an event type
void clear_type(event_type t) {
if(events[t] == NULL)
return;

while(events[t] != NULL && events[t]->read) {
if(events[t] == events_stack[t]) {
// This is the last one in the stack
delete events[t];

events[t] = events_stack[t] = NULL;

} else {
// Remove normally
event* e = events[t];
events[t] = e->next;

delete e;
}
}
}

// Clear all events
void clear() {
for(unsigned int a = 0; a < EVENT_COUNT && events != NULL; a++)
clear_type((event_type) a);
}

// Get a buffer of events of a given type
template <class T>
buffer<T> get(event_type t) {
if(events == NULL || events[t] == NULL)
return buffer<T>(NULL);

return buffer<T>(events[t]);
}
}

#endif

f_events::event_type is a list of event types used to put the right objects into the right event type arrays.

f_events::event is a parent struct used to hold data for an event. A child event would be something like this:
Code:
struct event_packet : public f_events::event {
channels channel;
ENetPacket* packet;

event_packet(channels c, ENetPacket* p) : f_events::event(f_events::NETWORK_PACKET) {
this->channel = c;
this->packet = p;
}

~event_packet() {
enet_packet_destroy(packet);
}
};

f_events::buffer is a convenience object for looping through events. Something like this:
Code:
f_events::buffer<T> event_buffer = f_events::get<T>(f_events::EVENT_TYPE);
while(event_buffer.next()) {
T* event = event_buffer.get();

// Do stuff with the event
}

Pushing an event is just a case of calling f_events::push() with an event child-class. The function will put the object into the right event array.

Clearing events will loop through events of the given type and delete all read events.
Logged

Yeah, that.
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic