Tuesday 3 July 2012

Generic XNA - Wiring your own Events


As I mentioned in one of the post responses on this blog here is an examples of how you can wire up events in your game object. As usual I have tried to keep this as simple as I can to try and make it clear whats going on.

I guess the first thing to do is describe what an event is, well an event can be anything that occurs during the execution of your game, an enemy shoots, a bomb detonates, the player passes the mouse over a game object (as in this example) but rather than checking ALL your objects each loop you just want to be told when this happens so you can do what you need to do to handle the event. I guess this is a pretty basic C# skill, but if you are not a C# developer you may not know how to do this, so here is my example of Events and how I wire them up.

So off to the code, I created a new project called GenericXNAEvents, off this project I created the object I am going to use in this example, a simple object that will display a coloured rectangle and have events attached to it that tell me when the mouse is passed over it, clicked on it and when the mouse leaves it. The first thing I do is create a delegate that defines my event, I make this public so it is accessible outside of my class as this dictates how the event handler will be written by those "subscribing" to the events. This done I then create my events, these are logical instances of the delegate that are going to be used to "Fire" my events when they occur, "Subscribers" will also be able to attach (wire) them selves to these events.

// Delegate definition of an event.
public delegate void FiredEvent(object sender);
// Instances of delegate event.
public FiredEvent OnMouseOver;
public FiredEvent OnMouseOut;
public FiredEvent OnMouseClick;

As you can see I have three events, one for when the mouse is over this object, one for when it moves off this object and one for when this object is clicked.
Now, I create the conditions that fire my events in the Update method.

// Is the mouse over me?
Point mouseCoord = new Point(Mouse.GetState().X, Mouse.GetState().Y);
Rectangle mouseRec = new Rectangle(mouseCoord.X, mouseCoord.Y, 1, 1);

Rectangle me = new Rectangle(left, top, width, height);

// Check for mouse over and moust out.
if (me.Intersects(mouseRec))
{
moueOver = true;

// If someone has subscribed to this event, tell them it has fired.
if (OnMouseOver != null)
OnMouseOver(this);
}
else
{
if (moueOver)
{
moueOver = false;
// If someone has subscribed to this event, tell them it has fired.
if (OnMouseOut != null)
OnMouseOut(this);
}
}

// Check for mouse clicked.
if (moueOver && Mouse.GetState().LeftButton == ButtonState.Pressed)
{
// If someone has subscribed to this event, tell them it has fired.
if (OnMouseClick != null)
OnMouseClick(this);
}

First thing I do is create the rectangles that will be used to check for intersection with the mouse, if the mouse intersects the object then I set my boolean flag marking that the mouse is over me to true, then check to see if anyone has subscribed to my OnMouseOver event, if they have I call there event handlers. If I am not intersecting the mouse and the mouse has been over me, then the mouse must have moved off of me, so I set my flag to false, check to see if anyone has subscribed to my OnMouseOut event, if they have I call there event handlers. Finally, if the mouse is over me and it has been clicked I check if anyone has subscribed to my OnMouseClick event and if they have, call there event handlers.

So that's the mechanics of it, how do I now subscribe to these events??

In the constructor of my Game1 Class I create three instances of my EventObjects, placed at different locations on the screen with different names and colours, I then "wire" up my event handlers.

EventObject obj1 = new EventObject(this, "Obj1", Color.Red, 100, 150, 32, 32);
EventObject obj2 = new EventObject(this, "Obj2", Color.Gold, 100, 500, 64, 32);
EventObject obj3 = new EventObject(this, "Obj3", Color.Silver, 400, 200, 64, 64);

// Wire up mouse over events.
obj1.OnMouseOver += new EventObject.FiredEvent(MouseOver);
obj2.OnMouseOver += new EventObject.FiredEvent(MouseOver);
obj3.OnMouseOver += new EventObject.FiredEvent(MouseOver);

// Wire up mouse out events.
obj1.OnMouseOut += new EventObject.FiredEvent(MouseOut);
obj2.OnMouseOut += new EventObject.FiredEvent(MouseOut);
obj3.OnMouseOut += new EventObject.FiredEvent(MouseOut);

// Wire up mouse click events
obj1.OnMouseClick += new EventObject.FiredEvent(MouseClick);
obj2.OnMouseClick += new EventObject.FiredEvent(MouseClick);
obj3.OnMouseClick += new EventObject.FiredEvent(MouseClick);

I now need to create my event handlers, these handlers MUST have the same pattern (return value and parameters) as the FiredEvent delegate defined earlier or you will get a compile error. Also note that I have appended (+=) the event handlers to the objects events, this means you can have a single event fire more than one event handler, you can associate a single handler if you like (=), and this will replace ALL previously set handlers to the one you specify.

public void MouseOver(object sender)
{
if (sender is EventObject)
{
message = string.Format("Mouse is over {0}", ((EventObject)sender).Name);
}
}

public void MouseOut(object sender)
{
message = "No Message.";
}

public void MouseClick(object sender)
{
if (sender is EventObject)
{
message = string.Format("Mouse clicked {0}", ((EventObject)sender).Name);
}
}
As you can see I am checking to see what the sender is before I do anything with it, you could have objects with a similar event definition call this event handler also, but in this case we just have the one.

So effectively you can have an event call multiple event handlers and have event handler called by multiple objects and object types. Pretty cool eh?! Thanks Microsoft :)

Full solution example can be found here.

Any questions, please post them here and I will do my best to answer them.

No comments:

Post a Comment