Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

Dark GDK / [DarkGDK Tutorial] Event Handlers

Author
Message
Zotoaster
19
Years of Service
User Offline
Joined: 20th Dec 2004
Location: Scotland
Posted: 16th Jun 2008 19:56 Edited at: 16th Jun 2008 21:22
Event Handling Tutorial for DarkGDK By Zotoaster


Welcome to my newest tutorial. This is actually my first one for DarkGDK, and infact it's not exactly specific to the GDK, but more for C++. I hope to cover some of the basics of event handling, what it is, how to use it, and how it can be applied to games. This is a very useful technique to make your game easier to produce and maintain.


Note: You will need some basic experience of C++ to understand this tutorial


1) What is an "event handler"?

An event handler does what it says on the tin - it handles events. When you think of a 3D object, you think of something solid. You can pick it up, move it around, spin in, texture it, scale it, etc. But an event is a little more abstract - it's something that happens. An event may infact consist of a series of different (or similar) events, all happening at the same time.
3D objects are very easy to handle, but events aren't. When something important happens in your game you may think you'd need to hard code it. For example, if you walk into a building you might want everything to be silent. When you get to the end you press a button, and as you run out you want explosions to occur around you as you pass by the relevant points. You dont want the building to explode as you walk in. That wouldn't make sense. It might seem to you that the only way to achieve this is by coding individual levels, one by one.
But you don't have to code an object everytime you use it. Everytime you rotate it a little, you don't have to program the vertices to move, and then render it to the screen. How can we apply this useful thinking to something as abstract as events? This is where event handlers come in.

Imagine being able to take an event. Screw around with it. Change it, play with it, etc, as if it were an object. Well, this is what this tutorial is all about.





2) The components of an event handler

The way to handle events is a lot simpler than you might think. Let's begin by looking at real life events. Do they ever just happen? By that I mean do they ever happen without a cause? If they didn't, random events would happen all the time for no reason. So we know that we must have certain triggers to set off these events, and thus, we must find a way to formalise these triggers that link events. Events and triggers are basically the only two things you have to worry about when handling events. The two are almost completely the same in terms of structure and the way you make them. The only main difference is that a) Triggers are checking mechanisms, whereas events are being told to happen, rather than being checked to, 2) Triggers link events, but events don't (unless the event itself is a trigger).

Both triggers and events are built up from two basic ingredients: types of event/trigger, and parameters. This is exactly the same when it comes to objects (atleast in GDK). You have certain object types, such as cube, sphere, mesh, etc., and certain parameters that follow, such as position, angle, etc. If you wanted two cubes, one the right way up and the other tilted to the side, you wouldn't make 8 vertices to represent one cube and position them accordingly, then make another 8 for the next and position them differently. No, you would simply make two cubes with the same default angles, and then rotate one of them. The same goes for events and triggers. Let's say you wanted this to happen: you stand infront of a door and it opens, but at the same time you want all your nearby allies to turn against you and want to kill you.

We'll go through it step by step:

- You make a trigger. This trigger has a type, and it has parameters. In this instance, the type is a box in 3D space that checks if you are standing within it. It would only have two parameters: One for the position of the trigger, and one for the size.
- You make an event. The event type is to open the door. The parameter is the door that you wish to open (this could be a number representing the object number that the door is, or you could pass an object (an OOP object of course) that represents the door that contains all the data about it. It doesn't matter which you do).
- You make another event. The type would be to turn your allies against you. The parameter would be an OO (object oriented) object representing you. You would have to specify yourself, otherwise they would all start killing each other since they are no longer in the same team).
- Finally, you get the trigger's list of events, and add these two to it.

When the trigger detects you walking in the box, the door will open and your friends will betray you.




3) The basic setup of an event handler

Lets look at triggers and events in a little more detail before we figure out how to code them. To start off with, we'll look at events.

--The event--

The first thing you have to do is know how you are going to have your different event types. I would go with an enumerator (enum in C++). These are perfect for storing a list of integers that you don't have to worry about the value. The next thing you have to do is know how to store your parameters. An std::vector is perfect for that. The funny thing about parameters is that you have to have lots of different datatypes. The easiest way to do this is set up a list for each parameter type that you add, then set up the main parameter list. This list has to have two pieces of data: the parameter type (which you can set up with a different enum), and then an integer, who's basic function is to point to a position in any list. The list it points to ofcourse is given in the first piece of data. Then all you have to do is compile this all into a single class.

From a heirarchial point of view, it might look like this:


(The coding bit we will come to later).


--The trigger--


Now the trigger is basically *exactly* the same as far as we are concerned right this moment, but requires an extra piece of information. This would be the list of events that you intend to call when you set the trigger off.


Again, from a heirarchial point of view, it might look like this (take notice of the extra piece of information in the trigger):






4) Programming an event handler in C++


Finally, we get to the fun bit, I hear you say. Damn right, I reply.

The event handler is really not too difficult to code, so we will get straight to it.

Before we go into the individual components, we will start by enumerating our data-types. This is just a basic list. You can add whatever types you wish in the future, such as particle systems and players.


With that out of the way, let's concentrate on the event.


--Programming the event--

We start obviously will the event types:


Then, onto the class (we love you C++):


A few notes: Firstly, you have to include <vector> into your project for this. Second, you can add as many lists here as you list, each for a different data-type (make sure you also add an identifier for it to the data-type enumerator). Thirdly, notice the last list. The data-type it uses is std::pair<tDataType, size_t>. std::pair<> is a very simple container class that can take two different pieces of data of different types, and can be referenced by .first and .second. size_t is a short way of making an unsigned integer (an integer that can't be negative).

Now that the class has been defined, the functions are easy to make:



Finally, we need to make a function that executes the event.



Let's look at this line of code in particular:


It seems to have this type of structure: Player[index].Die(). I made this up just for the purposes of showing how parameters work. The index is what we're interested in here.
The index in this case is this: iParam[ Param[ 0 ].second ]

Let's take it apart from the outside in so we can understand it. We have iParam[index]. This will just return one of the integers that you have used as a parameter. The exact index where we have this parameter is held within the actual parameter list. We have used the first parameter: Param[ 0 ], but remember, the parameter list has two associated pieces of data. We only want the second, which points to the correct place in the integer parameter list.

You may be thinking, what's the point in the first piece of data? Well, sometimes you might want the event to have different parameters. For example, I might make the event check if it is a string we are checking for, in which case it would act differently to if it was an integer. In this case for example, I might pass the player number as a parameter, or I might pass the player's name. Either way, you get a different reaction to it.



--Programming the trigger--


This is where it gets boring. The trigger is basically the same as the event (as we already know), so to cut down my workload I'm just going to copy at paste

Trigger types:


The class:


Notice the extra function and the extra list. There's no point in showing you the code for the parameter functions, and there's probably not much point in showing you the event functions, but I'll do it anyway:




Now, we add an extra function in the private section of the class


This will basically check if the trigger has been set, with respect to the parameters that the trigger requires the check data with.
This is a private function because it is only used by the trigger system itself, however, you can make it a public function if you with to check if a trigger is set... but why would you wish to? This is the entire point of an event system, you don't have to check for triggers manually.

Finally, we just make a handling function. All it does is check for the trigger, and then does what it needs to do when it has been set. Very easy:


Of course, this just checks for the trigger. If it has been set, set up a simple loop to go through all the events in the list of events, and set them off.


Here is a basic example of it at work now:


And that is how one codes an event system.




5) How can I make this useful?

The triggers and events here are very basic. In the event and trigger type enums you can easily add a different type of event. Inside the Execute() (for events) and Triggered() (for triggers) functions, you simply check if that is the given type, you do something different. Adding event and trigger should happen as you work on the game more and more, and you start to make your players, particle systems, doors, etc. It is probably impossible to have it all done early without having all your structs and classes already working, but it is important to get the foundations down early if you wish to make your levels work.




6) How can I take this further?

There is one obvious problem with the current system we have: for every single system you make you have to add another line to your loop telling it to be handled. This can be countered very easily by making an event system class, where you make all your events and triggers as usual, and then add the triggers to a list in the event system. Then, in your main loop, simply tell it to handle itself, and it will handle everything properly for you.

These event systems are very useful for different missions in your games. You can have the same level twice but with different missions. The great thing about this type of event system is that you can literarly save these missions (the events and the triggers) to a file, that can also link to a level file. This means you can actually have stuff happen in your game without needing to hard code it, and without needing to script it. Imagine this, hypethetically:



This would automatically mean that things would happen that are essential to the plot but without having to hard code it - pretty awesome eh?

The format for these missions wouldn't be difficult either:


Event1 and Event2 are simply followed by the associated data. The trigger is followed by the associated data, a line "======" to show that you are now about to read events, and then the events that it wants to use (Event1 and Event2). Load this into your game, make it handle itself, and there you go!






Hope you enjoyed my tutorial. Hope it helped, hope it gave you some new ideas, and I hope it makes your game making life easier for you.

Thanks.

Don't you just hate that Zotoaster guy?
Lilith
16
Years of Service
User Offline
Joined: 12th Feb 2008
Location: Dallas, TX
Posted: 16th Jun 2008 20:50
This is going to take some time to get through but the effort you put into it is appreciated.

Thanks.

Lilith, Night Butterfly
I'm not a programmer but I play one in the office
dark coder
21
Years of Service
User Offline
Joined: 6th Oct 2002
Location: Japan
Posted: 16th Jun 2008 21:07 Edited at: 16th Jun 2008 21:10
Your method may be beneficial for a scripting system but it seems a bit inefficient for just calling functions from within the program directly, I wrote this basic event handler to show another approach that requires less steps to add a new event, of course it's easy to break if you pass the wrong data.



Zotoaster
19
Years of Service
User Offline
Joined: 20th Dec 2004
Location: Scotland
Posted: 16th Jun 2008 21:28
That seems pretty good DC, but everytime you want a new event you have to make a struct to hold the data and the event function. I guess with mine you have to change the code aswell, but I don't know how satisfied I would be with having to make new functions with numbers in them Mine was pretty much specifically designed with loading them in mind. Yours is probably more efficient in terms of speed, but I think mine is a little more efficient in terms on maintainability.

Don't you just hate that Zotoaster guy?
Zotoaster
19
Years of Service
User Offline
Joined: 20th Dec 2004
Location: Scotland
Posted: 23rd Jun 2008 21:30
*bump* Anyone else read it?

Don't you just hate that Zotoaster guy?
dbGamerX
16
Years of Service
User Offline
Joined: 23rd Nov 2007
Location:
Posted: 23rd Jun 2008 21:55
That's a really good tutorial. You're making me doubt how much C++ I actually know! lol!

Mahoney
15
Years of Service
User Offline
Joined: 14th Apr 2008
Location: The Interwebs
Posted: 24th Jun 2008 01:41
Quote: "*bump*"


He, makes me think of 4chan.
Zotoaster
19
Years of Service
User Offline
Joined: 20th Dec 2004
Location: Scotland
Posted: 24th Jun 2008 02:10
dbGamerX, glad you enjoyed it.

Mahoney, bumping is used on all forums.

Don't you just hate that Zotoaster guy?
Mahoney
15
Years of Service
User Offline
Joined: 14th Apr 2008
Location: The Interwebs
Posted: 24th Jun 2008 02:14
I know. It just made me think of it. The technology forum /g/ is actually alright.
elantzb
15
Years of Service
User Offline
Joined: 10th May 2008
Location: Classified
Posted: 24th Jun 2008 08:59
lol.... 4chan...
Mahoney
15
Years of Service
User Offline
Joined: 14th Apr 2008
Location: The Interwebs
Posted: 24th Jun 2008 09:21
What?
elantzb
15
Years of Service
User Offline
Joined: 10th May 2008
Location: Classified
Posted: 24th Jun 2008 09:31
weird stuff on that site
Mahoney
15
Years of Service
User Offline
Joined: 14th Apr 2008
Location: The Interwebs
Posted: 24th Jun 2008 09:46
It depends on which board. Just avoid boards like /h/, /s/, /hc/, and usually /b/.
dbGamerX
16
Years of Service
User Offline
Joined: 23rd Nov 2007
Location:
Posted: 25th Jun 2008 04:28
Quote: "/h/, /s/, /hc/, and usually /b/."


Please explain.. What are the right slashes for and what do the letters stand for?

Mahoney
15
Years of Service
User Offline
Joined: 14th Apr 2008
Location: The Interwebs
Posted: 25th Jun 2008 04:35
That is how they label each image board on 4chan. /b/ is random. The others are best left unsaid. That's why I said to avoid them.
dbGamerX
16
Years of Service
User Offline
Joined: 23rd Nov 2007
Location:
Posted: 25th Jun 2008 04:44
Ahh, I see what you mean

Mahoney
15
Years of Service
User Offline
Joined: 14th Apr 2008
Location: The Interwebs
Posted: 25th Jun 2008 04:49
Yeah. Just avoid all but /g/.
Zotoaster
19
Years of Service
User Offline
Joined: 20th Dec 2004
Location: Scotland
Posted: 26th Jun 2008 02:43
You guys, take it somewhere else. This is my tutorial thread, not a 4chan substitute.

Don't you just hate that Zotoaster guy?
Mahoney
15
Years of Service
User Offline
Joined: 14th Apr 2008
Location: The Interwebs
Posted: 26th Jun 2008 03:47
Sorry. My apologies.
dbGamerX
16
Years of Service
User Offline
Joined: 23rd Nov 2007
Location:
Posted: 26th Jun 2008 05:51

Login to post a reply

Server time is: 2024-03-29 07:46:56
Your offset time is: 2024-03-29 07:46:56