#include <windows.h>
#include <string.h>
#include <iostream>
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
enum e_eptype {
EPTYPE_FLOAT,
EPTYPE_FLOAT3,
EPTYPE_DWORD,
EPTYPE_UNKNOWN = 0xffffffff
};
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
class EntityProperty {
private:
e_eptype m_Type;
char m_szPropertyName[32];
int m_iOffset;
public:
EntityProperty();
EntityProperty(const char* szName, const e_eptype type, const int iOffset);
char* GetName();
e_eptype GetType();
int GetOffset();
};
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
EntityProperty::EntityProperty() {
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
EntityProperty::EntityProperty(const char* szName, const e_eptype type, const int iOffset) {
memset(m_szPropertyName, 0, 32);
strcpy(m_szPropertyName, szName);
m_Type = type;
m_iOffset = iOffset;
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
char* EntityProperty::GetName() {
return m_szPropertyName;
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
e_eptype EntityProperty::GetType() {
return m_Type;
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
int EntityProperty::GetOffset() {
return m_iOffset;
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
class Entity {
friend class EntityFactory;
private:
float m_fPos[3];
float m_fDir[3];
char m_szClass[32];
public:
char* GetClass();
virtual void Test() {};
};
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
char* Entity::GetClass() {
return m_szClass;
}
#include <vector>
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
class EntityFactory {
private:
static std::vector<EntityFactory*>* m_pFactories;
static EntityFactory* GetFactory(const char* szClass);
int SetupProperties();
virtual Entity* CreateNewEntity() = 0;
char* GetClass();
private:
std::vector<EntityProperty*> m_Properties;
protected:
char m_szClass[32];
char* m_szPropertyInfo;
public:
EntityFactory(char* szEntityClass, char* szPropertyInfo);
static Entity* CreateEntity(const char* szClass);
static int GetEntityProperties(Entity* pEntity, EntityProperty* pProperties, int *iMax);
static int SetEntityProperty(Entity* pEntity, const char* szProperty, const float fValue);
static int SetEntityProperty(Entity* pEntity, const char* szProperty, const DWORD dwValue);
static int SetEntityProperty(Entity* pEntity, const char* szProperty, const float* f3Value);
};
std::vector<EntityFactory*>* EntityFactory::m_pFactories = 0;
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
EntityFactory::EntityFactory(char* szEntityClass, char* szPropertyInfo) {
if (m_pFactories == 0) {
m_pFactories = new (std::vector<EntityFactory*>);
}
strcpy(m_szClass, szEntityClass);
m_szPropertyInfo = strdup(szPropertyInfo);
SetupProperties();
m_pFactories->push_back(this);
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
char* EntityFactory::GetClass() {
return m_szClass;
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
int EntityFactory::SetEntityProperty(Entity* pEntity, const char* szProperty, const float fValue) {
if (pEntity == 0)
return 0;
EntityFactory* pF = GetFactory(pEntity->GetClass());
if (pF == 0)
return 0;
std::vector<EntityProperty*>::iterator i;
for (i = pF->m_Properties.begin(); i < pF->m_Properties.end(); ++i) {
EntityProperty* pP = *i;
if (strcmp(szProperty, pP->GetName()) == 0) {
char *pObj = ((char*) pEntity) + pP->GetOffset();
if (pP->GetType() == EPTYPE_FLOAT) {
*((float*)pObj) = fValue;
return 0;
}
}
}
return -1;
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
int EntityFactory::SetEntityProperty(Entity* pEntity, const char* szProperty, const DWORD dwValue) {
return -1;
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
int EntityFactory::SetEntityProperty(Entity* pEntity, const char* szProperty, const float* f3Value) {
return -1;
}
/*-------------------------------------------------------------------
szPropertyInfo:
PropertyName;PropertyType;OffsetInsideObject;PropertyName;PropertyType;OffsetInsideObject;...
-------------------------------------------------------------------*/
int EntityFactory::SetupProperties() {
char *szTmp = strdup(m_szPropertyInfo);
char *szToken;
char *szSeps = ";";
int iMember = 0;
char* szPos[3];
szToken = strtok(szTmp, szSeps);
while(szToken != NULL) {
szPos[iMember++] = szToken;
iMember %= 3;
//All members have been read out of trokens
if (iMember == 0) {
e_eptype type = EPTYPE_UNKNOWN;
if (strcmp(szPos[1], "FLOAT") == 0) {
type = EPTYPE_FLOAT;
} else if (strcmp(szPos[1], "FLOAT3") == 0) {
type = EPTYPE_FLOAT3;
} else if (strcmp(szPos[1], "DWORD") == 0) {
type = EPTYPE_DWORD;
}
int iOffset = -1;
iOffset = atoi(szPos[2]);
if (type != EPTYPE_UNKNOWN && iOffset >= 0) {
EntityProperty* pP = new EntityProperty(szPos[0], type, iOffset);
m_Properties.push_back(pP);
}
}
szToken = strtok(NULL, szSeps);
}
free(szTmp);
return 1;
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
Entity* EntityFactory::CreateEntity(const char* szClass) {
EntityFactory* pFactory = GetFactory(szClass);
if (pFactory == 0)
return 0;
Entity* pE = pFactory->CreateNewEntity();
strcpy(pE->m_szClass, pFactory->m_szClass);
return pE;
}
/*-------------------------------------------------------------------
pEntity pointer to entity to get properties from
pProperties pointer to an array to receive properties
iMax size of array at pProperties (receives number of properties written to pProperties
RETURN: maximum number of properties
-------------------------------------------------------------------*/
int EntityFactory::GetEntityProperties(Entity* pEntity,
EntityProperty* pProperties,
int *iMax)
{
if (pEntity == 0)
return 0;
EntityFactory* pF = GetFactory(pEntity->GetClass());
if (pF == 0)
return 0;
std::vector<EntityProperty*>::iterator i;
int idx = 0;
for (i = pF->m_Properties.begin(); i < pF->m_Properties.end() && idx < *iMax; ++i) {
EntityProperty* pP = *i;
pProperties[idx] = *pP;
idx++;
}
*iMax = idx;
return (int) pF->m_Properties.size();
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
EntityFactory* EntityFactory::GetFactory(const char* szClass) {
if (m_pFactories == 0)
return 0;
std::vector<EntityFactory*>::iterator i;
for (i = m_pFactories->begin(); i < m_pFactories->end(); ++i) {
EntityFactory* pFactory = *i;
if (strcmp(pFactory->GetClass(), szClass) == 0) {
return pFactory;
}
}
return 0;
}
/*-------------------------------------------------------------------
-------------------------------------------------------------------*/
template <class EntityClass>
class EntityFac : public EntityFactory {
public:
EntityFac(char* szClassName, char* szPropertyInfo) : EntityFactory (szClassName, szPropertyInfo) { };
Entity* CreateNewEntity() {
Entity* pE = new EntityClass ();
return pE;
};
};
class Player : public Entity {
private:
float fTest;
};
int main() {
EntityFac<Player> efacPlayer("Player", "health;FLOAT;60");
Entity * pE1 = EntityFactory::CreateEntity("Player");
Entity * pE2 = EntityFactory::CreateEntity("Player1");
EntityProperty p[10];
int m = 10;
int i;
EntityFactory::GetEntityProperties(pE1, p, &m);
while (m--) {
std::cout<<p[m].GetName() << " - " << p[m].GetType() << " - " << p[m].GetOffset() << "\n";
}
i = EntityFactory::SetEntityProperty(pE1, "health", 100.00333f);
std::cin;
return 0;
}
Game entities and their proprities
Hi,
I have some problems implementing an entity system for my game engine.
What i want to do is to have a baseclass CEntity which holds information
needed for all Entities like position, direction,...
class CEntity {
private:
float fPos[3];
float fDir[3];
char* szClassName;
public:
CEntity(char* szClass) : szClassName(szClassName) {};
}
From this baseclass more specialised entities are derived:
class CPlayer : public Entity {
private:
float fHealth;
int iMoney;
}
I create this entities by a factory class,so that i can call
Entity* pE = Factory::Create("Player");
The Factory looks up the class name and creates an object of the
desired class. This all works fine!
The problem I am having is to set the different Members inside an object.
For example I want to be able to do the following (mainly because of scripting):
pE->SetProperty("Health", 1000.0f);
To achive this i store a "member map" inside each factory, that holds
the information that the class "CPlayer" has the property "Health" which
is a float and starts at the offset 28 bytes.
As soon as I insert virtual methods inside CEntity, all my offset-tables
have to be shifted by 4 bytes because of the virtual function table created
inside CEntity.
So this aproach doesn't seem to be too well suited for what I want to do!
(It looks kind of evil to me :) )
Here is the code I used to test this all:
Anyone else wanted to do some similar bevor and has come to a useable solution for this?
Thanks in advance
Mathias
Well, in your instance you may want to have a "type" field or something like that, unless you want people assigning health values to ammo crates.
So check that your entity is of the right type, then you can typecast it by doing *((Player*)&entity) and change the health parameter.
So check that your entity is of the right type, then you can typecast it by doing *((Player*)&entity) and change the health parameter.
You do know that you can use the offsetof(Type, member) macro? It saves quite a lot of time when dealing with stuff like this. What you'll want to do is something like this:
Hope that helps :)
edit: I forgot to mention that the offsetof() macro only really works for public members. Unfortunately, this makes it less useful as it breaks the whole point of private vs. public accesses, but at least it takes into account virtual function tables.
[Edited by - FReY on February 10, 2005 5:45:58 AM]
EntityProperty* pP = new EntityProperty("health", EPTYPE_FLOAT, offsetof(CPlayer, fHealth));
Hope that helps :)
edit: I forgot to mention that the offsetof() macro only really works for public members. Unfortunately, this makes it less useful as it breaks the whole point of private vs. public accesses, but at least it takes into account virtual function tables.
[Edited by - FReY on February 10, 2005 5:45:58 AM]
Hi,
> EntityProperty* pP = new EntityProperty("health", EPTYPE_FLOAT, offsetof(CPlayer, fHealth));
This is very much of what i have been looking for. I cant cast a pointer of CEntity to CPlayer, since I dont know the class at runtime without looking at the m_szClass member. And I dont want to end with a ugly big chunk of if-else-statements!
What I want to be able is the following:
Lets say in my World-Editor (not even started yet..) I want to be able
to do s.th like:
CEntity* pEntity = World::Intersect(SomeRay);
pEntity->EnumProperties();
Show a dialog that lets the user modify all members of the intersected Entity.
Is there another way to solve this problem? Or is this a common method to do this?
Hope you all understand what I am trying to too!
Thanks Again
Mathias
hi there,
you could implement a COM-like QueryInterface method:
you could implement a COM-like QueryInterface method:
class BaseClass{public: virtual void* GetInterface(int IID) { if (IID == IID_BASECLASS) return this; else return NULL; }};class Player : public BaseClass{public: void* GetInterface(int IID) { if (IID == IID_PLAYER) return this; else return BaseClass::GetInterface(IID); }};class BotPlayer : public Player{public: void* GetInterface(int IID) { if (IID = IID_BOTPLAYER) return this; else return Player::GetInterface(IID); }};main(){ BaseClass* pObj = new Player(); BotPlayer* pBot = (BotPlayer*)pObj->GetInterface(IID_BOTPLAYER); //will return NULL Player* pPlayer = (Player*)pObj->GetInterface(IID_PLAYER); //will point at player}
I gave this Code to Sauruman once. You're welcome to have a look at is if you will find it useful.
It's the WIP implementation of my Entity class. Each entity has a propertyset, a message handler and can be part of a heirarchy.
I'm currently working to add 'class' definitions definiable by XML to allow serialisation of entities to and from an XML document.
Let me know if this was useful or if you need anything explaining. I'm still working on the code, so I'm looking for suggestions to improve it if you have any. Like I said, feel free to use it.
It's the WIP implementation of my Entity class. Each entity has a propertyset, a message handler and can be part of a heirarchy.
I'm currently working to add 'class' definitions definiable by XML to allow serialisation of entities to and from an XML document.
Let me know if this was useful or if you need anything explaining. I'm still working on the code, so I'm looking for suggestions to improve it if you have any. Like I said, feel free to use it.
Hello everyone
@FReY:
I didnt know the macro offsetof! This is realy great by using this I don't have to calculate the offset by myself and more important: I can add new nembers inside a class and the offset in the factory-definition are updated automaticaly!
@evolutional:
I have been reading through your code just right now, sadly I am in a hurry and call look at it in detail right now. But it seem like there is a major difference between our implementations:
In your code, every entity (even if it is from the same class) can have different properities. Also, your proprities are no class members defined by compile time, but more variables dynamicaly allocated and registerd at runtime. So you alway have to call s.th. like GetFloatProperty("Health") to get the health value of an object - even inside methods of your class. eg:
In the method I am trying to get working I can simply do:
Or have I missed some parts of your code?
It is very interessting to read anyway, since I haven't thought on how to implement message handlers for my Entities!
I will make some more test with my code and the use of offsetof.
If u like, I will post future successes here to let u all benefit from it!
Greez
Mathias
@FReY:
I didnt know the macro offsetof! This is realy great by using this I don't have to calculate the offset by myself and more important: I can add new nembers inside a class and the offset in the factory-definition are updated automaticaly!
@evolutional:
I have been reading through your code just right now, sadly I am in a hurry and call look at it in detail right now. But it seem like there is a major difference between our implementations:
In your code, every entity (even if it is from the same class) can have different properities. Also, your proprities are no class members defined by compile time, but more variables dynamicaly allocated and registerd at runtime. So you alway have to call s.th. like GetFloatProperty("Health") to get the health value of an object - even inside methods of your class. eg:
bool Player::AmIdead() { return (this->GetFloatProperty("Health") < 0.0f ? true : false);}
In the method I am trying to get working I can simply do:
(inside class methods)bool Player::AmIdead() { return (this->m_fHealth < 0.0f ? true : false);}and:(from outside the class)float f = (Player*) p->GetProperty("Health");
Or have I missed some parts of your code?
It is very interessting to read anyway, since I haven't thought on how to implement message handlers for my Entities!
I will make some more test with my code and the use of offsetof.
If u like, I will post future successes here to let u all benefit from it!
Greez
Mathias
I would have to say I prefer evolutional's system to yours. I guess I am just a sucker for homogeneous notation of that sort. In my opinion, it is a non-issue that he must treat properties as dynamic things that cannot be referenced statically in member functions ... because that is EXACTLY his intention, for the property to be completely dynamic. Now, it is easy to take "data-driven" too far, but I dont really see that happening in this case.
Quote:Original post by MathiasW
In your code, every entity (even if it is from the same class) can have different properities. Also, your proprities are no class members defined by compile time, but more variables dynamicaly allocated and registerd at runtime. So you alway have to call s.th. like GetFloatProperty("Health") to get the health value of an object - even inside methods of your class. eg:
Well, technically with my system you'll be able to use it in a similar way to yours. I've added the ability to bind class members to the property set so you can use them with the property accessors. Of course, you can also create properties at runtime too.
The goal of my system is, as TRE said, to create a data-driven entity class. My final aim is to have it so that I can define an entity class in XML which is created at runtime. Obviously, I'll need some hardcoded classes if only for speed, but it'll allow the user to derive from the base (hard) class and extend it with their own properties if needed. I'm going to be expanding my entity really soon to handle the XML parsing and create my entity factory/manager to handle the entity classes.
My entity classes will be defined using XML and creation of a new entity will be a matter of creating an 'instance'. A new instance will have all the attributes of the template class but can be extended further if needed (via script or code).
The final, 'crucial' aim for me is to allow each entity to have scripted methods (which will probably be called via the event system). Again, these methods are added to the concrete entity classes at runtime and should allow overides to be made.
Quote:It is very interessting to read anyway, since I haven't thought on how to implement message handlers for my Entities!
Glad I could have been of some use. My message system is far from perfect and is still WIP, but it's a decent starting point.
It's nice to see that I'm not the only one playing around with propertysets for entites though [grin]
Quote:Original post by evolutional
The final, 'crucial' aim for me is to allow each entity to have scripted methods (which will probably be called via the event system). Again, these methods are added to the concrete entity classes at runtime and should allow overides to be made.
Just out of curiosity, what are your current plans for how to implement this?
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement