Implementing triggers and such

dbrock

27-02-2008 03:06:50

Hey guys,

I want to start implementing the trigger system for our game. My idea is to have the level editor place trigger boxes, which will act as the trigger cube, and have a few cegui options on what you'd like it to do, ie. kill the player, kill the enemy, open the door, change the map, play a cinematic, increase your stats, start a timer, stop a timer, etc. Would I have to create a new callback class for each of these different types of triggers? Or would it be more practical to pass an argument to the trigger class, and process the output of the trigger inside that class with basically a boat load of if statements.

We're kind of pressed for time on getting this game done at school (4 weeks left), so the shortest method, even if its not the cleanest method will have to do =[. Lots of our player input unfortunately has to get hacked in! But I'd like to show at some point at least what we've accomplished to this point with 3 beginner programmers, and myself who has more experience (but not with this size of a project - so the planning stages were difficult).

Any resources or examples on past trigger implementation would be greatly appreciated.

Thanks!

betajaen

27-02-2008 11:10:31

Okay, the easiest and most efficient way I can see doing this, is using function pointers and a trigger manager class.

The trigger manager class is the only class in your application which only creates, handles and deletes triggers.

Make three functions to handle the trigger states; Enter, Exit and Inside. You can name those three Functions with the pattern of OnActorEnter, onActorExit and onActorInside. Now every trigger you make, the callback goes to the TriggerManager using the OnActorEnter, OnActorExit, OnActorInside. Make sure you use the method pointer callback and not the inherited one (one is a template function and one isn't - you want the template one).

Since every time you want those triggers to call different parts of your program, you can use a function pointer. If your unhappy with creating your own mini-function pointer class you can use boost function, either way, you want three of these:

std::map<Actor*, FunctionPtr> onActorInsidePtr

So when your trigger is fired, just get it as so:

void TriggerManager::onActorInside(Trigger* trigger, Actor* actor) {
FunctionPtr fptr = onActorInsidePtr[Trigger];
fptr.GoAndRunThisFunctionForMe(trigger, actor);
}


I think that's it. Obviously you'll have to add the createTrigger, destroyTrigger functions to your TriggerManager class but that's trivial.

dbrock

27-02-2008 19:30:22

I really appreciate the input, I'm going ahead now to create the TriggerManager, and I'll post my experiences with it, perhaps it'll help others later on.

Edit: Does this look correct? Ignore the Ogre::String part, it's for the sake of compiling at the moment until I gather a boost function ptr or something in it's place.

namespace DeadCold
{
class TriggerManager : public NxOgre::TriggerCallback::ContactCallback
{
private:
typedef std::map<NxOgre::Actor*, Ogre::String> TriggerActorInsideMap; // String will be replaced by function ptr
typedef std::map<NxOgre::Actor*, Ogre::String> TriggerActorEnterMap; // String will be replaced by function ptr
typedef std::map<NxOgre::Actor*, Ogre::String> TriggerActorExitMap; // String will be replaced by function ptr

TriggerActorInsideMap m_OnActorInsidePtr;
TriggerActorEnterMap m_OnActorEnterPtr;
TriggerActorExitMap m_OnActorExitPtr;

public:
TriggerManager( void );
virtual ~TriggerManager( void );

public:
void createTrigger( void );
void destroyTrigger( void );

private:
void onEnter( NxOgre::Trigger* a, NxOgre::Character* b );
void onInside( NxOgre::Trigger* a, NxOgre::Character* b );
void onLeave( NxOgre::Trigger* a, NxOgre::Character* b );
};
}

nullsquared

28-02-2008 00:49:30

I really appreciate the input, I'm going ahead now to create the TriggerManager, and I'll post my experiences with it, perhaps it'll help others later on.

Edit: Does this look correct? Ignore the Ogre::String part, it's for the sake of compiling at the moment until I gather a boost function ptr or something in it's place.

namespace DeadCold
{
class TriggerManager : public NxOgre::TriggerCallback::ContactCallback
{
private:
typedef std::map<NxOgre::Actor*, Ogre::String> TriggerActorInsideMap; // String will be replaced by function ptr
typedef std::map<NxOgre::Actor*, Ogre::String> TriggerActorEnterMap; // String will be replaced by function ptr
typedef std::map<NxOgre::Actor*, Ogre::String> TriggerActorExitMap; // String will be replaced by function ptr

TriggerActorInsideMap m_OnActorInsidePtr;
TriggerActorEnterMap m_OnActorEnterPtr;
TriggerActorExitMap m_OnActorExitPtr;

public:
TriggerManager( void );
virtual ~TriggerManager( void );

public:
void createTrigger( void );
void destroyTrigger( void );

private:
void onEnter( NxOgre::Trigger* a, NxOgre::Character* b );
void onInside( NxOgre::Trigger* a, NxOgre::Character* b );
void onLeave( NxOgre::Trigger* a, NxOgre::Character* b );
};
}

Sure, it looks right. Just a note - you might as well use direct function pointers, if you wish:

typedef void (*TriggerFunc)(/* params */);
std::map<NxOgre::Actor*, TriggerFunc> foo;

betajaen

28-02-2008 01:12:30

It looks pretty good, and you could use those TriggerFunc as nullsquared, but if you want a method pointer in a class then your own function pointer struct/class, boost pointer or even my own class will do.

dbrock

29-02-2008 06:44:28

Make three functions to handle the trigger states; Enter, Exit and Inside. You can name those three Functions with the pattern of OnActorEnter, onActorExit and onActorInside. Now every trigger you make, the callback goes to the TriggerManager using the OnActorEnter, OnActorExit, OnActorInside. Make sure you use the method pointer callback and not the inherited one (one is a template function and one isn't - you want the template one).


When I create the trigger with
NxOgre::Trigger* tempTrigger =mScene->createTrigger(...);
tempTrigger->setCallback( this ); // Doesn't callback need to use the Inherited callback?


The part I'm confused with is setting the callback, cause I'm not sure which class to use to inherit from in my TriggerManager. Right now its using "class TriggerManager : public NxOgre::TriggerCallback::ContactCallback"

My TriggerManager.h looks like this at the moment:
namespace DeadCold
{
typedef enum
{
TRIGGER_DOOR = 0,
TRIGGER_KILL_SELF,
TRIGGER_KILL_OTHER,
TRIGGER_CINEMATIC,
TRIGGER_MINI_GAME,
TRIGGER_SOUND,
TRIGGER_MULTIPLE,
TRIGGER_CHANGE_STATS
} TriggerType;

class TriggerManager : public NxOgre::TriggerCallback::ContactCallback
{
private:
// Function callbacks
typedef void (*TriggerFunc)( NxOgre::Trigger*, NxOgre::Character* );

// Maps
typedef std::map<NxOgre::Character*, TriggerFunc> TriggerCharacterInsideFunc;
typedef std::map<NxOgre::Character*, TriggerFunc> TriggerCharacterEnterunc;
typedef std::map<NxOgre::Character*, TriggerFunc> TriggerCharacterExitMunc;

// Lists
typedef std::list<NxOgre::Trigger*> TriggerList;
typedef std::list<NxOgre::Trigger*>::iterator TriggerListIterator;

// Members
TriggerCharacterInsideFunc m_OnActorInsideFunc;
TriggerCharacterEnterunc m_OnActorEnterFunc;
TriggerCharacterExitMunc m_OnActorExitFunc;
TriggerList m_TriggerList;
NxOgre::Scene* m_PHYS_Scene;

public:
TriggerManager( NxOgre::Scene* );
virtual ~TriggerManager( void );

public:
/* Create/Destroy */
NxOgre::Trigger* createTrigger(
TriggerType type,
const Ogre::String& name,
const Ogre::Vector3& dimensions,
const Ogre::Vector3& position
);

bool destroyTrigger( const Ogre::String& name );
bool destroyTrigger( NxOgre::Trigger* trigger);

public:
/* Game Triggers */
void triggerCinematic( const Ogre::String& args );
void triggerSound( const Ogre::String& args );
void triggerKillSelf( const Ogre::String& args = "" );
void triggerKillEnemy( const Ogre::String& args );
void triggerMultiple( const Ogre::String& args );
void triggerMinigame( const Ogre::String& args );
void triggerDoor( const Ogre::String& args );
void triggerChangeModel( const Ogre::String& args );
void triggerChangeStats( Enemy* enemy, const Ogre::String& args );
void triggerChangeStats( Player* player, const Ogre::String& args );

private:
/* Trigger Callbacks */
void onActorEnter( NxOgre::Trigger* a, NxOgre::Actor* b );
void onActorInside( NxOgre::Trigger* a, NxOgre::Actor* b );
void onActorLeave( NxOgre::Trigger* a, NxOgre::Actor* b );

void onCharacterEnter( NxOgre::Trigger* a, NxOgre::Character* b );
void onCharacterInside( NxOgre::Trigger* a, NxOgre::Character* b );
void onCharacterLeave( NxOgre::Trigger* a, NxOgre::Character* b );
};
}


How I implemented createTrigger ( incomplete )

NxOgre::Trigger* TriggerManager::createTrigger(
TriggerType type,
const Ogre::String& name,
const Ogre::Vector3& dimensions,
const Ogre::Vector3& position
)
{
NxOgre::Trigger* trigger = m_PHYS_Scene->createTrigger(
name,
new NxOgre::CubeShape( dimensions.x, dimensions.y, dimensions.z ),
position,
""
);

/* Confused at this part *
* ********************* *

trigger->setCallback(
&TriggerManager::onActorEnter,
&TriggerManager::onActorInside,
&TriggerManager::onActorLeave,
&TriggerManager::onCharacterEnter,
&TriggerManager::onCharacterInside,
&TriggerManager::onCharacterLeave
);

or should it be

trigger->setCallback( this );

* ********************* */

m_TriggerList.push_back( trigger );

return trigger;
}

betajaen

29-02-2008 10:49:28

You can use the inherited callback if you wish, but make sure the functions exactly match those in the InheritedCallback class else it won't work.