Gameengine: design question regarding entitys

Post your questions, suggestions and experiences regarding game design, integration of external libraries here. For irrEdit, irrXML and irrKlang, see the
ambiera forums
Post Reply
TheGameMaker
Posts: 275
Joined: Fri May 12, 2006 6:37 pm
Location: Germany

Gameengine: design question regarding entitys

Post by TheGameMaker »

Somehow I came over the question of how to build a good entity managment.. (it somewhat keeps me thinkingy -_-)

Idea:
an Entity Manager that got a map/vector/tree of entitys (maybe somewhat organised as a Quadtree or stuff). via manager->register(MyEntity) one can add an (derivated class) Entity to this map. The manager now will call certain events for this all the entitys. (Collision, update, interaktion etc) Those events are virtual Functions one can modify.
Disatvantage: every event will be passed to every Entity -_-
There might be some ways around this (having a list of all entitys that are interested in a special event or something, delegates etc)
(I'd allways would like to have suggestions)

Each entity need to have a way to comunicate with the others entitys propertys -> for example calling members or modifying some variables.
Example: We got a explosion entity that will damage every other entity that will collide with it. So we have the collision callback collided(Centity *other)
now assume this callback is triggered for the spaceship -> whatever
No we need to figure out what kind of collision did happen. The class Centity will simply have some ID (either a String or a Int) now we are able to access this value of the xplosion entity since its part of Centity and thus other->ID works. Now we determin other->ID=="explosion"
*Edit* maybe using RTTI? ->dammit slow??
The next step would be to figure out some internal value (lets say the strength.) So we got a few ways to make this values accessable:

1) We make some public values available in Centity. For example lets say 20 public floats (lets call 'em skills) -> this is the method used by the A6 engine. I guess this is the most retarded way of handeling this.

2) We create some virtual functions in Centity that will take a variable name and return its value. ->suxx since one always would have to build this function and returnvalues have to be a fixed type -_-

3) we have to cast Centity* other to CentityExplosion* to access its internal values. (I think this is the best version.. but still suxx)

4) another better way ;)
which way would you say might be the best?
Everytime I get a new Idea I will find a whole bunch of disatvantages so I was not able to come to some kind of conclusion ^^
Is there any good litrature/papers around regarding entity Managment etc. ??
greetz TGM
rogerborg
Admin
Posts: 3590
Joined: Mon Oct 09, 2006 9:36 am
Location: Scotland - gonnae no slag aff mah Engleesh
Contact:

Post by rogerborg »

The same answer that I always give:

If you're working to changeable requirements, sure, go hog wild on a flexible architecture.

If you're working to your requirements, then invest your time on requirements and paper design instead, then implement the minimum required to satisfy those requirements and design.

In this case, work out what your most complex game objects need to do, then see if there's anything that would benefit from being generalised and abstracted. Don't start from the point of view of writing an all singing, all dancing generalised entity and then specialising it.
Please upload candidate patches to the tracker.
Need help now? IRC to #irrlicht on irc.freenode.net
How To Ask Questions The Smart Way
TheGameMaker
Posts: 275
Joined: Fri May 12, 2006 6:37 pm
Location: Germany

Post by TheGameMaker »

well, I do not plan writing an engine... Its just I'm curious how this might be done the best/most elegant way... :P
So lets assume someone would want to desing that ueber entity-engine, what would be the way to do it.. (wll, I know there are several ways as always, and no one turns out to be the "best", "greatest" etc. But there will be some most flexible)
Seven
Posts: 1034
Joined: Mon Nov 14, 2005 2:03 pm

Post by Seven »

I have always preferred to use a robust messaging system for entity to entity communication. It allows me to do a multitude of things.

1) log all events for debugging
2) time delay the messages (example, guard notices player and I want to simulate delayed reaction. send message to self saying to change states to attack, but delay the deliveryt time.
3) obects dont need to know about each other. this one is important. the fireball spell just sends a message to all affected objects (objects in range) and the objects react acordingly. Paper will burn, a metal sword will do nothing and the fire golem will grow back a few hit points. example at bottom.

Code: Select all

// a simple message class
class CSObjectMessage
{
public:
	ADD_PUBLIC(int,m_Type,Type);			// message type
	ADD_PUBLIC(int,m_Type2,Type2);			// message type
	ADD_PUBLIC(int,m_Source,Source);		// who initiated this message
	ADD_PUBLIC(int,m_Dest,Dest);			// who is the message going to
	ADD_PUBLIC(float,m_RTime,RTime);		// what time was the message sent	
	ADD_PUBLIC(float,m_DTime,DTime);		// what time to deliver the message
	
	char m_Data[255];
	char* GetData() { return m_Data; };
	void SetData(char* data) { strcpy(m_Data,data); };
	
	CSObjectMessage();						// class constructor
	virtual ~CSObjectMessage();			// class destructor
	
	// dual creation allows for better error handling
	virtual bool Create(int type, int type2, int source,int dest, float rtime, float dtime, char* data);	
};


I go so far as to use the messages to pump the state manager of the objects. it allows me to log literally everything that gets sent to the objects.

Code: Select all

void CSObject::Frame()
{
	Debug_Logf("CSObject::Frame()");
		if (next_state != state)
		{
			if (force_state_change)
			{
				SendObjectMessage(MSG_RESERVED_Exit,0,GetId(),GetId(),0,0,"");
				state = next_state;
				next_state = state;
				force_state_change = false;
				SendObjectMessage(MSG_RESERVED_Enter,0,GetId(),GetId(),0,0,"");
			}
		
		SendObjectMessage(MSG_RESERVED_Update,0,GetId(),GetId(),0,0,"");
	}		
}

Code: Select all

bool CSObject::ReceiveObjectMessage(CSObjectMessage* m)
{
	Debug_Logf("CSObject::ReceiveMessage(%d %d %d %d %f %f)",m->GetType(),m->GetType2(),m->GetSource(),m->GetDest(),m->GetRTime(),m->GetDTime());

	if (m == NULL) 
	{
		Debug_Logf(" message is NULL!!!");
		return false;
	}

	switch (m->GetType())
	{
		case MSG_NULL			 :			
		case MSG_RESERVED_Enter	 : 	// Don't send this message - the system sends this when a state is first entered - Use OnInitialize to listen for it
		case MSG_RESERVED_Exit	 : 	// Don't send this message - the system sends this when a state is exited - Use OnExit to listen for it
		case MSG_RESERVED_Update :	// Don't send this message - the system sends this when a game tick goes by - Use OnUpdate to listen for it
		case MSG_Timeout		 :		
		case MSG_ChangeState	 :
			{
				Debug_Logf("CSObject:: Receiving message - going to process state machine");
				return ProcessStateMachine(m);
			} break;
	}

	return false;
	// we didnt want the message
	return false;
}

void CSObject::SendObjectMessage(CSObjectMessage* m)
{
	Debug_Logf("CSObject::SendObjectMessage(%d %d %d %d %f %f)",m->GetType(),m->GetType2(),m->GetSource(),m->GetDest(),m->GetRTime(),m->GetDTime());

	GetFactory()->GetMessageManager()->AddMessage(m->GetType(),m->GetType2(),m->GetSource(),m->GetDest(),m->GetRTime(),m->GetDTime(),m->GetData());
}

void CSObject::SendObjectMessage(int type, int type2, int source,int dest, float rtime, float dtime, char* data) 
{
	Debug_Logf("CSObject::SendObjectMessage(%d %d %d %d %f %f)",type,type2,source,dest,rtime,dtime);

	GetFactory()->GetMessageManager()->AddMessage(type,type2,source,dest,rtime,dtime,data);
}




Code: Select all

bool CSObject_FireGolem::ReceiveObjectMessage(CSObjectMessage* m)
{
	Debug_Logf("CSObject::ReceiveMessage(%d %d %d %d %f %f)",m->GetType(),m->GetType2(),m->GetSource(),m->GetDest(),m->GetRTime(),m->GetDTime());

	if (m == NULL) 
	{
		Debug_Logf(" message is NULL!!!");
		return false;
	}

	switch (m->GetType())
	{
		case MSG_DAMAGE :	
			{
				// determine the type of damage
				switch (m->GetType2())
				{
					case DAMAGE_FIRE	: /* make the golem stronger */ ; break;
					case DAMAGE_WATER	: /* no impact to a fire golem */ ; break;
					case DAMAGE_COLD	: /* make the golem weaker */ ; break;
					case DAMAGE_ACID	: /* do normal damage */ ; break;
				}
			} break;
		case MSG_COLLISION :	
			{
				// do whatever collision code is done
			} break;
		case MSG_WHATEVER :	
			{
				// do whatever collision code is done
			} break;
	}

	return false;
	// we didnt want the message
	return false;
}
Dorth
Posts: 931
Joined: Sat May 26, 2007 11:03 pm

Post by Dorth »

(Thanks for sharing like that btw :) Might need a code snippet )
TheGameMaker
Posts: 275
Joined: Fri May 12, 2006 6:37 pm
Location: Germany

Post by TheGameMaker »

well, thats quite a good idea ;).. I guess this is the best idea ^^
Post Reply