Re: Modeling events that occur in a game world




"Aaron J. M." wrote:

An event queue was what I thought up at first. I thought of having a
generic Event class that goes into this queue, and there'd be
subclasses like AttackEvent, DeathEvent, BurningRobotAppearsEvent,
etc. The problem with that is how the information attached to each
specific event is lost. The Visitor pattern looked like it may
address this, considering that there are several potential
EventVisitors such as the AI or the GUI. However, not all parts of
the system are likely to be interested in every single type of event,
especially if I come up with several kinds of events.

Your idea for events having unique IDs is interesting. It doesn't
help with data carried with the event though. Rather than have a
unique ID and try to unpack the information like you suggested, it may
seem simpler to just check the type of the event object directly and
then cast it. That may lead to a lot of case statements though.

Unpacking the information would be the responsibility of the objects
receiving the event. It would look something like this in C#:

enum EventID
{
Attack,
Defeat,
RobotAppears,

// Other event ID's...
}

public class GameEventArgs : EventArgs // EventArgs is a .NET base class
// for all event classes.
{
private EventID id;

public GameEventArgs(EventID id)
{
this.id = id;
}

public EventID ID
{
get
{
return id;
}
}
}

public class AttackEventArgs : GameEventArgs
{
public AttackEventArgs() : base(EventID.Attack)
{
}

// Other stuff...
}

And we could have several more event classes that are derived from the
GameEventArgs class.

We have our event queue and objects interested in registering to be
notified when an event occurs; we could do something like this:

EventQueue eventQueue = new EventQueue();
Creature someCreature = new Creature();

eventQueue.Register(EventID.Attack, someCreature);

The Creature class would implement an interface, as would all classes
that are interested in receiving events:

public interface EventListener
{
void ProcessEvent(GameEventArgs e);
}

When the event queue dequeues an event, it checks the event's ID and
notifies all objects interested in that event by passing the
GameEventArgs object to them. It's then up to the receiver of the event
to unpack it, i.e. cast it to its specific type:

public class Creature : EventListener
{
public void ProcessEvent(GameEventArgs e)
{
switch(e.ID)
{
case EventID.Attack:
{
AttackEventArgs attackEvent = (AttackEventArgs)e;

// Do something in response to the attack...
}
break;

default:
Debug.Fail("Unknown event");
break;
}
}
}

Each class knows what events it's interested in, so it switches on only
those event ID's.

BTW, it's best if event objects are immutable, that way there's no harm
in passing the same event object to multiple receivers.

If having each class implement a switch on the ID seems excessive to
you, as it may to some, you can still use the Visitor approach but give
Visitor class do nothing implementations for each of the visit methods.
Derived classes only override those methods specific to the events they
are interested in. A drawback to this approach is that if you're using a
language that only supports single inheritance, you lose the ability to
have your class derived from another class as it has to derive from the
Visitor class.




.