functors & central event system help

Started by
7 comments, last by LorenzoGatti 15 years, 5 months ago
Hi, I'm trying to create a central event system for my game where I add some events to a priority_queue and pull off the first one as soon as it expires. But I'm have a problem of understand if I'm adding them to the queue correctly, if they're in order, and how to pull them off. Can anyone lend a hand here? Here I initialize my priority queue.

	my_queue = new MyEventQueue();
Then I create 10 events adding the current time and a generated random time between 0 and 15 seconds (so that the event executes x number of seconds from the time they are created) and put them on a queue.

     for (int i = 0; i < 10; i++) {
         MyEvent *event = new MyEvent();
         event->set_start_timestamp(); // now time
         event->set_execute_timestamp(); // time to execute from now

         my_queue->push_event(event);
     }
Then in each frame of my game, I check the queue to see if the event is ready to be executed.

    if (my_queue->count() > 0) {
        MyEvent *evnt = my_queue->top_event();

         // is_expired() is how i was doing it, but didn't work
        if (evnt->is_expired()) { 
            execute_event(evnt->id());

            my_queue->pop_event();
        }
    }
And here are my event classes, and this overloading is really confusing me, not sure this is even close to correct.

class MyEvent
{
	...

    public:
		...
        bool is_expired();
        struct timeval start_timestamp();
        double execute_timestamp();
};

struct EventPointerCompare {
    bool operator() (MyEvent* lhs, MyEvent* rhs) const {
        return ( lhs->_execute_time < rhs->_execute_time );
    }
};

// this is my priority_queue wrapper
class MyEventQueue
{
    private:
        priority_queue<MyEvent *, vector<MyEvent *>,
            EventPointerCompare> event_queue;

	public:
		check_queue();
		has_events_of_type_x();
		...
Can anyone help me understand and what I need yet to do to make this work? [Edited by - Wizumwalt on October 22, 2008 1:25:51 AM]
Advertisement
Why a priority queue and not a stack?
I will constantly add events to the queue throughout the life of the program and need for the event w/ the least amount to be the one I pull off. The times are generated randomly and I don't control how they get put on the queue. And more can be added at anytime w/ a lesser timestamp.
I would say you're putting too many things in the same basket. Keep things simple.

An event is an action that has to be executed. As such, it could be for instance a mere typedef boost::function<void ()> event;. This allows reusing boost::bind and all the other various techniques for creating events, because an even is something very simple.

Your application probably wants to execute events at certain points in time. This is the task of a scheduler, which is used to schedule the execution of an event at a certain point in time. Note that the event does not know what time it was set to be executed at: this is the responsibility of the scheduler, not of the event.

The scheduler can be implemented by internally defining a scheduled_event structure which contains an event and its execution time, and storing a priority queue (or two, if you're using an at-/before- scheduling convention) of these structures sorted by execution time.

So, in a gun-related function:
event killMonster = boost::bind(&Monster::Die, this -> target);this -> context.scheduler.at(NOW + 3.0, killMonster);


And in your main loop:
time += step_length;this -> context.scheduler.run(time);
Ok, this turns out to be a really good reason for me to learn boost.

So I'm trying to create 10 events that will start from a random amount of time between now and 10 seconds. I have the random generating code function that works. And I'm trying to have a scheduler execute my events at *that* randomly generated time.

I have this at the top of one of my *.cpp files
	typedef boost::function<void ()> event;


Then here I create the event which I think binds the die function of the Monster class to this void function called 'killMonster'. What does the second argument do? This just creates the event and schedules it? Is this context.scheduler a part of boost or ... where can I find more about creating a scheduler?
	event killMonster = boost::bind(&Monster::die, this -> target);	this -> context.scheduler.at(NOW + 3.0, killMonster);


Then I have a function that is called between every frame of my game, so that's where I planned on checking the time of the event to see if it had expired. Can someone elaborate as to this ... I don't understand how that fits in with step_length.
	time += step_length;	this -> context.scheduler.run(time);
How will u guarentee that the monster will still be valid when the event executes. Boost bind holds a naked ptr to the monster but without some sort of refrence counting or other scheme, putting objects on a long lived queue to be executed later seems dangerous.

-ddn
Well, the monster is not going anywhere's so ... I don't see why it would go away. I don't plan on deleting it.

Now I'm wondering what you mean and if I should go back to a priority_queue.
If you know 100% that those objects on the queue will never be deleted during the lifespan on the queue, then it's fine. But in general with more complex programs you can't make that assumption.

So people use indriect refrences like handles or some sort of reference counting sceheme. Some lanagues have garabge collection (like C# and Java) so they don't bother with either of those schemes, but for C++ you'll need some sort of system inplace if you want to use this event engine in a more generic manner.

It's good idea, though i suggest using like this instead of boost bind

http://www.codeproject.com/KB/cpp/FastDelegate.aspx
http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx

Last time i meaured it, these delegates are about 10x faster than the boosts veraion. These are what I use in my event engine as well.

Good Luck!

-ddn
There can be several events per simulation step, I hope you mean something like
 while (my_queue->count() > 0) {        MyEvent *evnt = my_queue->top_event();         // is_expired() is how i was doing it, but didn't work        if (evnt->is_expired()) {             execute_event(evnt->id());            my_queue->pop_event();        }else{            break;        }    }

Omae Wa Mou Shindeiru

This topic is closed to new replies.

Advertisement