Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411656 Posts in 69395 Topics- by 58451 Members - Latest Member: Monkey Nuts

May 15, 2024, 04:43:21 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)How do you handle triggers in your game engine?
Pages: [1]
Print
Author Topic: How do you handle triggers in your game engine?  (Read 5135 times)
MrGando
Level 0
***



View Profile WWW
« on: April 27, 2010, 11:44:11 AM »

Hello people,

I'm very interested to know how everyone that use triggers ( if anyone at all use them ), is handling them.

By a trigger I mean:

E.g:

Explosive Box:

if(player walks close)
     this.triggerEvent();

I ask this, because I'm very interested to know about the internals, how is people creating trigger systems ? Also, I think it would be ideal to let the trigger system, to somehow associate a trigger to an action/event ?

For example, if the "explode" action/event associated with the ExplosiveBox is called "FireExplosion" , it would be nice to separate the FireExplosion logic from the engine internals, and put that into scripts ( Lua/Json/XML, etc )

 Smiley

PS: I work with C++ mainly.
Logged

Lead Programmer
Gando Games
Twitter
Rob Lach
Level 10
*****



View Profile WWW
« Reply #1 on: April 27, 2010, 02:55:18 PM »

How you use triggers is really a case by case basis. If your game is extremely trigger heavy you'd use a more elaborate system, and if you have very little emphasis on triggers you'd have another system.

Recently I've be using a event system (triggers sends out events, handlers pick them up). To extend this to a scripting system is pretty easy and actually one of the targeted uses of them.
Logged

Chris Whitman
Sepia Toned
Level 10
*****


A master of karate and friendship for everyone.


View Profile
« Reply #2 on: April 27, 2010, 03:01:55 PM »

I use a polling system. (Lua scripted) objects that have collision behavior invoke their parent level objects with a collision area and the name of a callback, and then this callback is invoked once for every object in the collision area (the object collided with is passed in).

But then, this is more of an adventure game, so it requires a fair amount of scripting in interactions.
Logged

Formerly "I Like Cake."
LemonScented
Level 7
**



View Profile
« Reply #3 on: April 27, 2010, 05:29:36 PM »

I've got an event system, like Pierog. It got discussed a fair bit starting from page 2 in this thread: http://forums.tigsource.com/index.php?topic=11685.0

I work mostly in C++, although I use XML for data (like for defining game entities and laying out levels). I don't do much in the way of scripting, because I think that scripting is mostly useful in big, content-heavy games developed by big teams and I'm currently a one-man shop. But event systems can tie in well to scripting languages, I think, because of how they keep parts of the game logic seperate. The thing that fires the event doesn't need to know about the things that receive the event. It may only fire that event because some data file told it that it should. The listener can deal with the event in code, or pass it off to a scripting language, which I guess could fire events of its own.
Logged

MrGando
Level 0
***



View Profile WWW
« Reply #4 on: April 28, 2010, 05:40:13 AM »

Thanks for your answers guys,

Lemon, this piece of code was really useful for me :

Code:
void cPlayer::Init()
{
    EventHandle mGrenadeHandle = EventMgr().GetChannel(THROW_GRENADE_KEY).Attach(this, &cPlayer::OnGrenadeKey);
    // ...
}

void cPlayer::DeInit()
{
    EventMgr().GetChannel(THROW_GRENADE_KEY).Detach(mGrenadeHandle);
    // ...
}

void cPlayer::OnGrenadeKey(const bool & bPressed)
{
    if(bPressed)
    {
        if(mGrenadeCount > 0)
        {
            ThrowGrenade();
        }
    }
}

This is very interesting , and I got your idea perfectly, it's very similar to what I had been thinking about, I have two questions though:

1) How can you make the "Attach" method to be able to receive different function pointer ?? And how does the EnventManager know what parameter to send to that specific function ?

I mean, now you have a callback function that receives a bool, what if you wanted one that received nothing, or two bools, etc ?

For this I'm using Boost signals/slots, which adds a bit more safety than using just a function pointer. Still I have to do something like :

Code:
typedef boost::signal<void (int)> TriggerSignal; //Trigger signal is a "function pointer that returns void and receives int.

void Button::addSignal(const TriggerSignal::slot_type& slot) //assign a function pointer to the signal associated to this button.
{
this->aSignal.connect(slot);
}

void Button::call(int action) //Call get's called when my button is pressed.
{
this->aSignal(action); //Will call the performAction(action) method of some Character.
}

/*So, to assign some function to this class I go:*/
Button *aButton = new Button();
Character *aCharacter = new Character();
aButton->addSignal(boost::bind(&Character::performAction, aCharacter));

In the previous example I basically bound aButton to a Character "void performAction(int actionType)" method, when the button is pressed (not shown)
the performAction will be called for Character.

2) Could you illustrate on how would you handle this with the model you presented in the other post ? Smiley
« Last Edit: April 28, 2010, 03:13:04 PM by MrGando » Logged

Lead Programmer
Gando Games
Twitter
LemonScented
Level 7
**



View Profile
« Reply #5 on: April 28, 2010, 05:30:13 PM »

1) How can you make the "Attach" method to be able to receive different function pointer ?? And how does the EnventManager know what parameter to send to that specific function ?

My code doesn't actually have an EventMgr().GetChannel(THROW_GRENADE_KEY) ... although I could add that, I guess, I just did it to make the example cleaner. My code looks more like:

EventHandle mGrenadeHandle = ThrowGrenadeEvent.Attach(this, &cPlayer::OnGrenadeKey);

ThrowGrenadeEvent is statically declared somewhere, and is an EventChannel, which is a templated class, so declared something like:

static EventChannel<bool> ThrowGrenadeEvent;

So, the EventChannel has a templated type which is the kind of information it carries, and has an Attach method which feeds it a pointer to an object, and a pointer to a method in the class from which that object was instantiated - it stores those pointers in a list of listeners. When you call Send on that EventChannel, it iterates through the listeners, and does this for each one:

(mObject->*mMethod)(param);

where param is the templated type (bool in this case).

In my system, you wouldn't send two bools as part of an event, you'd send a single struct that contains two bools:

struct sTwoBools
{
    bool foo;
    bool bar;
};

EventChannel<sTwoBools> SomeTwoBoolsEvent;

The case when you want to send nothing (void) is a bit fiddly and I had to do a template specialisation thing with that. Basically you define a specialised version of EventChannel where the templated type is void and don't pass anything into the method pointer.

2) Could you illustrate on how would you handle this with the model you presented in the other post ? Smiley

I'm not 100% sure I understand the question, I've not used those features with Boost. From what I can see from the code you posted though, you're talking about adding a callback to aCharacter to some list in aButton, which presumably requires aButton to be derived from some base class which handles that list. My system doesn't quite work that way, because it has a third class called something like aButtonEventChannel, which is an EventChannel. aCharacter registers itself with that, and aButton would call aButtonEventChannel.Send() with some int as a parameter, and it'd be aButtonEventChannel's job to notify all of the listeners. The beauty of this system is that you can totally mix and match if you want to - aButton has no particular attachment to aButtonEventChannel, doesn't know how it works or who its registered listeners are, and can just send events on that channel (or any other one) without ever caring about what's at the other end. aButtonEventChannel doesn't care who called its Send method, and although it's got a list of listeners, it doesn't even know who registered those listeners. It's possible for a class to Attach() a listener by providing an object and a method pointer to some completely different class, if it so desires.

Does that make sense?
« Last Edit: April 28, 2010, 05:34:36 PM by LemonScented » Logged

MrGando
Level 0
***



View Profile WWW
« Reply #6 on: April 28, 2010, 07:04:40 PM »

yeah ! , it does make sense actually :-), I think mixing boost to avoid coding the template madness myself could help me, but I have been looking to implement a system like yours for quite a while!

Thanks for sharing your design  Smiley
Logged

Lead Programmer
Gando Games
Twitter
Jason Bakker
Level 2
**


View Profile WWW
« Reply #7 on: May 14, 2010, 03:52:38 AM »

I just use a fairly standard event system (at least it's standard as far as I know Tongue). Everything derives off Listener, and Listeners are added to an EventManager (not everything though, just stuff that does want to listen to something). Events call CreateEvent on event manager, which passes that Event through to the Listeners, each of which choose whether they care about that Event or not (Events have unique identifiers, have parameters, etc etc).

It's not the prettiest or least intensive system, but as long as you don't flood it with events it works fine for me!

Ed: Similar to LemonScented's system, but his is more elegant Wink
Logged

Triplefox
Level 9
****



View Profile WWW
« Reply #8 on: May 14, 2010, 06:35:55 PM »

I've tried both ad-hoc systems and monolithic "all events pass through this function" to enforce MVC. I've started to lean towards callbacks for the UI(out of sheer laziness, I'll probably have a "see-the-light" moment and get that code refactored someday), and am toying with ideas for a gameplay "event scheduler" that lets you build a DAG of nodes which events can get "trapped" and "released" from to enforce a particular order of events.

My reasoning for a scheduler is that some events you want to happen instantly, but others should be deferred because they'll affect something you're presently iterating over(for example, a collision event causes something to spawn and new collision to be created, when you haven't finished with the existing collision). And lot of events are "fire after so much time has passed" so again, you want them to just go into a general scheduler operation. Getting the order of operations right can really make a huge difference for things like turn-based games, in particular, so you end up wanting to have a very fine control over what happens right after an event is fired. Plus, if you defer things for several frames due to some unforeseen coding problem that makes initialization a multistep process, it hurts responsiveness. It's kind of a different problem from the GUI style of event system, where there isn't an emphasis on timing and interplay of state, and your main concern is just to enforce synchronization.

The Jason Gregory "Game Engine Architecture" has a great discussion of event systems for games.
Logged

Jacob_
Level 1
*



View Profile WWW
« Reply #9 on: May 15, 2010, 06:20:29 PM »

I have an abstract class Event with a fire() method that executes Lua code.

Here's the pseudo code for it:

Code:
class Event {
    public Vector connections = new Vector<Connection>(64);
    public void connect(LuaObject o, LuaState l) // o is a function
    {
        connections.put(new Connection(o, l));
    }
    //This can be overridden, some events use arguments with the connect function or other fancy stuff
    public void fire()
    {
         for(Connection c : connections)
         {
               //Push o onto the stack of l and call it
         }
    }
}

Then for an object:

Code:
class LuaBox { //LuaGameObject that goes with Box
    public CollideEvent collided = new CollideEvent();
}

The physics engine fires the event when needed; user scripts can access the connect function.
Logged
MrGando
Level 0
***



View Profile WWW
« Reply #10 on: May 17, 2010, 04:43:58 AM »

Nice to see so many answers,

in the end I aimed for something similar to LemonScented suggestion :-), but it's nice to see some good discussion about this feature.
Logged

Lead Programmer
Gando Games
Twitter
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic