It has been a while since I have updated, but I have a couple of posts I will make within the next week. The first post is on the enhancements I have made to our Event System.
The basic implementation of this event system is from lua programming gems (very nice book). It consists of a lua object (otherwise known as a table) that contains a couple of tables that stores Events, Pending Subscribers and Pending Unsubscribers, as well as a couple of functions for updating subscribers and firing events. You can get the full source for the Event System here (toward the bottom, 21 I believe). This Event system has a C++ implementation that simply pushes a string onto the stack and calls the lua function like so.
The Fire Event function looks something like:
function EventManager:FireEvent( eventName, ... ) self.Events[ eventName ] = self.Events[ eventName ] or {} for subscriber,_ in pairs( self.Events[ eventName ] ) do subscriber[ eventName ]( subscriber, ... ) end end
and in C++ looks like:
void FireEvent( lua_State* L, std::string eventName ) { //Call the lua FireEvent on this event manager object. lua_pushlightuserdata( L, this ); //STACK: em lua_gettable( L, LUA_REGISTRYINDEX ); //STACK: self lua_pushstring( L, "FireEvent" ); //STACK: "FireEvent", self lua_gettable( L, -2 ); //STACK: FireFunc, self lua_pushvalue( L, -2 ); //STACK: self, FireFunc, self lua_pushstring( L, eventName.c_str() ); //STACK: self, eventName, FireFunc, self lua_pcall( L, 2, 0, 0 ); //STACK: self }
As you may see, there is some lost functionality in this process. Although the lua fire event function takes additional arguments (note the “…” ), the C++ function does not account for this. This allows event data to be passed around at the lua level, but not from C++ to lua. This is the primary use of an Event! when you have keyboard and mouse input you would like to fire an input event that contains the bytecodes of the input used!
This is okay, with a little effort and C++ template magic we can fill in the little gap. we need to be able to pass in any type of data and include it in the arguments to lua like anything else.
In order to do this, we need 2 things.
First we need to change the C++ FireEvent function to a template function, allowing us to only write it once, and still be able to take many types of event data.
Secondly, we need to expose our EventData classes to lua, and be able to push each EventData type onto the lua stack. This can get very complicated, and as i said before, there are many ways to expose data to lua, this is just one of them.
I used luna (actually lunar but I copy and pasted some of it into my luna class). Luna is somewhat of a pain if you want to expose multiple things into your lua environment from C++, but I find that it is mostly copy and paste and chaging names once you got it down.
Here now, is the new code:
C++ FireEvent and Push Function for a collision event
template <typename T1> inline void fireEvent(std::string eventName, T1 a1) { EventLog << "FireEvent:: " << eventName << "\n"; //Call the lua FireEvent on this event manager object. lua_pushlightuserdata( L, this ); //STACK: em lua_gettable( L, LUA_REGISTRYINDEX ); //STACK: self lua_pushstring( L, "FireEvent" ); //STACK: "FireEvent", self lua_gettable( L, -2 ); //STACK: FireFunc, self lua_pushvalue( L, -2 ); //STACK: self, FireFunc, self lua_pushstring( L, eventName.c_str() ); //STACK: self, eventName, FireFunc, self PushArg(a1); lua_pcall( L, 3, 0, 0 ); } void PushArg(CollisionEvent* EV); void PushArg(PickEvent* PE); void PushArg(InputEvent * IE); //here is what the PushArg for a Collision event looks like and how the new fire event is //used. void EventManager::PushArg(CollisionEvent* EV) { Luna<CollisionEvent>::push(L, EV, true); } //Here is the Event System in action, using a Collision Event to pass collision data to lua. CollisionEvent* CE = new CollisionEvent(); void* id1 = pair.actors[0]->userData; void* id2 = pair.actors[1]->userData; std::cout << "collision:" << (*(int*)id1) << "::"<< (*(int*)id2) << std::endl; CE->Obj1_ID = (*(int*)id1); //GameObject ID set at actor creation CE->Obj2_ID = (*(int*)id2); Events->fireEvent<CollisionEvent*>("Collision" , CE ); //and lastly, the luna(r) implementation of a collision event #ifndef COLLISIONEVENT_H #define COLLISIONEVENT_H #include "luna.h" #include <iostream> using namespace std; class CollisionEvent { public: CollisionEvent(); CollisionEvent(lua_State *L); ~CollisionEvent(); static const char className[]; static Luna<CollisionEvent>::RegType methods[]; //static const Luna<Foo>::RegType Register[]; int getObj1ID(lua_State *L); int getObj2ID(lua_State *L); //private: int Obj1_ID; int Obj2_ID; }; typedef struct { CollisionEvent *pT; } CollisionEventdataType; #endif
Phew, quite a bit. Here is how that very collision event code example is handled by a subscriber in lua.
function Obj:Collision( CEvent ) t = CEvent:getObj2ID() d = CEvent:getObj1ID() print( t .. "," .. d .. ":and my ID:" .. self.ID ) end
Not too bad huh?!
In conclusion, you can see that template functions and classes can go a long way in making it easy to provide many kinds of event types for an Event System. In Particular, you see how the update function in this event system lends itself to such an implementation in C++. I hope you found this post useful! Next will be on quaternions!
DB
PS. thanks to dmail, ddn, and m_switch on the gamedev forums for the help in coming up with this.

