Game Code Complete Event Types

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Game Code Complete Event Types

      I still don't have my head around this. It's built upon this idea of hashing, and then in the game example at the end of the book the event handler does string compares.

      What gives here? If you are only going to use strings, why hash the event name into an integer? It doesn't appear to be used for anything.

      Ray
    • Ack! You are able to perceive a major inefficiency with the event system.

      It is true, we are wasting the hashed int value by using strcmp instead of the equality operator of the EventType class.

      The EventType class should not even have a getStr function. That entire event system should only acknowledge the string once, during construction of the EventType object and nevermore after that.

      I hope that clears that up.
    • Originally posted by Kain
      Ack! You are able to perceive a major inefficiency with the event system.

      It is true, we are wasting the hashed int value by using strcmp instead of the equality operator of the EventType class.

      The EventType class should not even have a getStr function. That entire event system should only acknowledge the string once, during construction of the EventType object and nevermore after that.

      I hope that clears that up.


      Thanks for the reply. How then does the event handler get the int value for the comparison with the event type? Does it re-hash the string again?

      Still confused :P
    • The string never needs to get rehashed after construction of the EventType object.

      If you go to 'class EventType' in the sample codebase, you will notice that the 'm_ident' member var gets created upon construction of the 'EventType' object.

      operator== for 'EventType' basically does an equality check on this 'm_ident' var. In other words, that int value you were asking about was there all along and just not being used.

      If anything, class EventType should get rid of the 'm_identStr' member var because it is only there as a convenience for logging/debugging purposes.

      So if you're in an optimizing mood, you're going to want to make your code store the EventType objects that you made for your own events as a static EventType and not a static string. Then, inside the HandleEvent function of your EventListener classes, you'll be replacing ...

      Source Code

      1. if( 0==stricmp( event.getType().getStr(),Evt_Move_Actor::gkName) )

      with

      Source Code

      1. if( event.getType()==Evt_Move_Actor::staticEventType )
    • Thanks again for your reply. That makes more sense, but can you guess my next question already?

      The reason the author gives for having this event type structure instead of just an enumeration is so you don't have to recompile all modules that use the events when you add a new event. However, I am assuming the class definitions for these events reside in a header file, and that header file needs to be included in all modules that use the event classes.

      How is the author justifying this statement?
    • and that header file needs to be included in all modules that use the event classes.

      You are right about every event needing a corresponding header file, but the quote above is the part that needs some more explaining.

      Let's say you have 100 event types. Event1, Event2.. all the way to Event100.

      Now let's say you have a header file for each event. Event1.h, Event2.h, ... Event100.h.

      If you were to use enums instead of an EventType class, you'd probably have the enums declared in a central header file called EventDeclarations.h or something. This is probably the situation you are seeing. So every time you add a new event, you must go to that enum and add another entry to the enum declaration... and now everything that included 'EventDeclarations.h' will have to recompile.

      The key here is that not everything that included 'EventDeclarations.h' needed to know about all 100 event types. Some of the things that included 'EventDeclarations.h' need to know only Event24, while others only need Event42. You get the idea.

      So with the EventType class, what's accomplished by 'EventDeclarations.h' gets separated into a hundred different files... one header file for each event. Now whatever needed to know about Event42 only needs to #include "Event42.h". If you were to add a new event,Event101, then you only create one new header file, "Event101.h". Adding this file and using it does not force anything else to recompile except whatever needs Event101.

      So to revisit your quote above, the header file does NOT need to be included in all modules that use event classes. There is more than one header file, and it only needs to be included on a per event basis. :)

      Using an EventType structure accomplishes what an enumeration would accomplish, but with the ability to define the structures independently of each other (no shared enum declaration).
    • That makes more sense, thank you. One last point, then I promise I'll leave you alone! :P

      Why have the EventType class at all? Why not just declare a public member in the say CreateActorEvent class...

      const static DWORD EventType=HashString("evt_moveactor");

      If all the EventType is really holding is the hashed int value, then it seems a little redundant.
    • Don't leave me alone... nobody ever talks to me. They'd rather talk to other organic carbon-based constructs.
      If all the EventType is really holding is the hashed int value, then it seems a little redundant.

      True. The idea here is that you get a benefit from strong typing. If you use DWORD instead of event type, then nothing is preventing you from accidentally comparing an event type to NULL, for example.

      Also, you get a major human-proofing benefit if you make the default constructor of EventType private to the EventType class. Now any programmer who instantiates an EventType is forced to specify something for the constructor. No accidental default EventType created with invalid parameters.

      For example...

      Source Code

      1. const EventType MoveActorEvent::staticType = EventType();

      ...won't compile, you must specify an identifying label.

      Source Code

      1. const EventType MoveActorEvent::staticType = EventType("evt_moveactor");


      If you were just using a DWORD or EventType with a public default constructor, then there would be nothing to stop a programmer from compiling an implicitly constructed EventType or DWORD that could lead to a hard to catch bug... as compile errors are always easier to catch than runtime errors.
    • Thanks again for the great explanation. I now have my event declared as...

      Source Code

      1. // create actor event
      2. class CreateActorEvent:public Event
      3. {
      4. protected:
      5. public:
      6. static EventTypePtr StaticEventType;
      7. CreateActorEvent(std::string in_name, std::string in_filename, Vector3 in_position, Vector3 in_rotation, Vector3 in_scale)
      8. :Event(System::GetTime(),
      9. StaticEventType,
      10. new CreateActorEventData(in_name, in_filename, in_position, in_rotation, in_scale)) {}
      11. };
      Display All

      and initialized in the module with

      Source Code

      1. EventTypePtr CreateActorEvent::StaticEventType=new EventType("evt_createactor");

      my comparison now looks like...

      Source Code

      1. if (*in_event->GetEventType() == *CreateActorEvent::StaticEventType)

      All this means the hashing is only taking place once, and all comparisons should be done by using the ID instead of the string.

      I must confess that I am still not seeing the benefits of the strong type casting as being that great. The event types are only declared in one place, so I would say the scope for error was pretty small and as for comparing to null, why would you do that when you would be comparing with a static member anyway?

      Thanks again!
    • Looks good, except you don't need that static EventType to be a pointer (or smart pointer). You can just declare the EventType as a non-pointer. Declaring static or global variables that make heap allocations is not really a good idea, but that's another subject.

      As for the benefits of strong type casting, human programmers are capable of anything; I've learned not to trust them. But this is one lesson that can never be told; it must be lived.
    • Great stuff. Thanks, Kain.

      Maybe Mike will update his code in the third edition :D. I am really enjoying the book as a whole. I like the way he has these systems modularized and interacting via the event manager. Makes the code a lot easier to work with.

      I am reading through the Eberly book too, so I am taking what I think are the best ideas from each to make my cross-platform engine (cross-platform is another thing that I hope the third edition will lean towards).
    • Hey whaddaya mean?

      I was gainfully employed for BOTH books. The first edition I was working at Compulsive and Glass Eye, then for the 2nd edition I actually worked at BreakAway.

      I only THOUGHT I was going to be unemployed the whole time I wrote the 2nd edition - because I signed the contract right after Ion Storm.
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • I've got one thing to say right now:

      Mike, you rock. Reading your book has solved the very key issue that I was having at this time. As a student at DigiPen, I have to work with a team. And the event system that we're using for this year's project (first year) is good enough in that it works. However, we have it where the coder has to manually specify unique types, which is very very very annoying and the only way to make the code readable, is through use of #defines which clutters up everything.

      My question is this: why is there an interface for the Event Manager? I can't really think of a need to extend multiple types of global Event Managers as they would all practically do the same thing. What would be the benefit of initially declaring an interface that the global Event Manager would then have to extend?
      Feel you safe and secure in the protection of your pants . . . but one day, one day there shall be a No Pants Day and that shall be the harbinger of your undoing . . .

      The post was edited 1 time, last by Tarviathun ().