CDerivedClass *der; der.special_member = 20; addtograph(der);Since ''special_member'' is not a member of CEntity, I can not access it. ex: if(ent1 collides with ent2) ent1 gains ent2''s say, fuel. ent1 and 2 being within the scene graphs list and having been converted to a standard CEntity... im sure the answer is right infront of my face, but due to lack of sleep and a physically demanding job, im to tired to see it.
🔒 Locked
c++ oop question...
Advertisement
Original Post
Hiya,
I have a problem that I can''t seem to figure out. First off, Ill state what im doing. Im working on my entity system right now and im planning on simply adding the specail physics routines straight to each entity class instead of creating routines outside of the class. The base entity class will have standard attributes such as position, velocity, rotation etc
and each sub class will have there own attributes. (ie. a vehicle class may have torgue, fuel amount, whatever).
The problem is, is that, well really, I can''t figure out how to add the entity to the scene graph and then access is data later on. ex:
Thanks a ton in advance guys, please don''t ignore this post
~Jason
Why not have all entities share the same
members and just ignoring those that
not apply to some entities??
Then using inheritance to set the
entities behaviour apart with the
methods.
I hope that made sense.
-------------------------------------------------
"The object of war is not to die for your country
but to make the other bastard die for his."
members and just ignoring those that
not apply to some entities??
Then using inheritance to set the
entities behaviour apart with the
methods.
I hope that made sense.
-------------------------------------------------
"The object of war is not to die for your country
but to make the other bastard die for his."
It sounds like you need some form of RTTI (Run-Time Type Identification). Turning it on for the whole code might be overkill though (additional information - a mangled class-name amongst others - is stored per class instance in memory).
You could roll your own for this specific case. One trick is to have a virtual 'GetType()' function for the CEntity class and CDerived, CDerived2, etc which returns a constant, eg TYPE_ENTITY, TYPE_BIKE, TYPE_CAR, TYPE_SOLDIER. Polymorphism will allow the right GetType to be called, so you will see the result of the GetType() from the derived class, even though itis via a CEntity pointer. You check the type and then cast the CEntity pointer to the correct derived type.
Hope this makes sense. I haven't had my morning coffee yet
Yours,
Archimage
(Ann-Marie Ratcliffe)
[edited by - Archimage on May 12, 2004 2:46:42 AM]
You could roll your own for this specific case. One trick is to have a virtual 'GetType()' function for the CEntity class and CDerived, CDerived2, etc which returns a constant, eg TYPE_ENTITY, TYPE_BIKE, TYPE_CAR, TYPE_SOLDIER. Polymorphism will allow the right GetType to be called, so you will see the result of the GetType() from the derived class, even though itis via a CEntity pointer. You check the type and then cast the CEntity pointer to the correct derived type.
Hope this makes sense. I haven't had my morning coffee yet
Yours,
Archimage
(Ann-Marie Ratcliffe)
[edited by - Archimage on May 12, 2004 2:46:42 AM]
hmmm. I had thought of that actually but then I thought that maybe it would take up to much memory. If you have a class that has 50 members of floats, structs etc, times all of that by the size of each member then times that by say 100 entity instances.
In the end, I quess it wouldn''t take very much extra memory. hmm. Im wondering how practical it would be really.
In the end, I quess it wouldn''t take very much extra memory. hmm. Im wondering how practical it would be really.
Im actually already using an rtti system. However, accessing members within a derived class from the base class isn't possible?
I think I had came up with a solution once a while back actually...
Hmmm. Ill play around with this.
[edited by - no one on May 12, 2004 2:50:10 AM]
I think I had came up with a solution once a while back actually...
void OnCollision(CEntity *pEnt)
{
mPosition = mOldPos;
CPlayer *player;
if(pEnt->mEntType == ePlayer)
{
player = (CPlayer*)pEnt;
Say("Dragon: You dare to approach me human!!");
player->mHealth -= 4;
}
pEnt = player;
}
Hmmm. Ill play around with this.
[edited by - no one on May 12, 2004 2:50:10 AM]
quote:
Original post by no one
Im actually already using an rtti system. However, accessing members within a derived class from the base class isn't possible?
You can't directly get at the member variables of a derived class via a pointer to the base class. You could of course have virtual Accessor functions common to all entities, and then where they make no sense to the derived class have them do nothing. Then polymorphism would help you. It's the flip side of Mutare's method. But you still have the hassle of either putting all the variables in the base-class (see Mutare's post), or putting all the accessor members in the base-class, and then over-riding as appropriate. Either way its putting information in the base-class that isn't relevant except to particular derived classes.
Hope this helps,
Archimage
(Ann-Marie Ratcliffe)
[edited by - Archimage on May 12, 2004 3:06:56 AM]
It sounds like you want to model interactions between pairs of Derived classes, where the type of both objects is significant.
Sounds like a job for double dispatch.
Sounds like a job for double dispatch.
I just did a test on this and it seems that by casting a entity pointer to some derived class, you can access the memebers and get there correct values...
Heres a small program I just made to test this...
I feel thats the answer. Now, ill just have to come up with a clean way of doing all of that nasty code.
Thanks.
Heres a small program I just made to test this...
#include <iostream>
#include <string>
using namespace std;
enum rtti
{
edefault,
ederived1,
ederived2,
};
class CBase
{
public:
CBase()
{
SetType(edefault);
mpInt = 1;
}
~CBase(){}
int mpInt;
rtti mpType;
virtual void SetType(rtti type)
{
mpType=type;
}
virtual void Disp(void)=0;
};
class CDerived1 : public CBase
{
public:
CDerived1()
{
SetType(ederived1);
}
~CDerived1(){}
float mpFloat;
void Disp()
{
cout<<"derived attribute "<<mpInt<<endl
<<"derived1 class''s attribute "<<mpFloat<<endl;
}
};
class CDerived2 : public CBase
{
public:
CDerived2()
{
SetType(ederived2);
mpChar = new char;
}
~CDerived2(){delete mpChar;}
char *mpChar;
void Disp()
{
cout<<"derived attribute "<<mpInt<<endl
<<"derived2 class''s attribute "<<mpChar<<endl;
}
};
CBase *g_list[3];
void Init()
{
CDerived1 *d1 = new CDerived1;
CDerived2 *d2 = new CDerived2;
CDerived2 *d3 = new CDerived2;
d1->mpFloat = (float)rand();
d2->mpChar = "whats up";
d3->mpChar = "test";
g_list[0] = d1;
g_list[1] = d2;
g_list[2] = d3;
}
int main(void)
{
Init();
g_list[0]->Disp();
g_list[1]->Disp();
CDerived1 *d;
if(g_list[0]->mpType == ederived1)
{
d = (CDerived1*) g_list[0];
d->mpFloat = 10.16;
}
g_list[0] = d;
g_list[0]->Disp();
float newvalue;
CDerived1 *d2;
if(g_list[0]->mpType == ederived1)
{
d2 = (CDerived1*) g_list[0];
newvalue = d2->mpFloat;
}
g_list[0] = d2;
cout<<"new value "<<newvalue<<endl<<endl;
char *name;
CDerived2 *d3;
if(g_list[2]->mpType == ederived2)
{
d3 = (CDerived2*)g_list[2];
name = d3->mpChar;
}
g_list[2] = d3;
cout<<name<<endl;
for(int i = 0; i < 2; i++)
delete g_list[i];
return 0;
}
I feel thats the answer. Now, ill just have to come up with a clean way of doing all of that nasty code.
Thanks.
quote:
Original post by no one
I just did a test on this and it seems that by casting a entity pointer to some derived class, you can access the members and get their correct values...
As I pointed out above, once you have cast the base pointer (Entity) to the correct Derived class, you can use all the members of Derived.
Just a thought, and this is probably overkill, but you could generalise this slightly by using multiple inheritance as a type of Interface system.
class IFuelSupply // Interface for entities that supply Fuel
{
public:
// Pure abstract functions
virtual int FuelStore() = 0; // How much fuel on board
virtual int ReFuel(int amount) = 0; // Take 'amount' of fuel from store, unless
// there is not enough, then take as much as
// you can. Returns total fuel taken.
};
class IAmmoSupply // Interface for entities that supply Ammunition
{
public:
// Pure abstract functions
virtual int AmmoStore() = 0; // How much Ammo on board
virtual int ReAmmo(int amount) = 0; // Take 'amount' of ammo from store, unless
// there is not enough, then take as much as
// you can. Returns total ammo taken.
};
// ... other interfaces.
class CEntity
{
...
};
class CFuelTanker : public CEntity, IFuelSupply
{
...
public:
// Must be defined, since they are abstract in interface
int FuelStore() { /* do whats necessary */ };
int ReFuel(int amount) { /* etc */ };
...
};
class CCar : public CEntity
{
...
public:
void OnCollision(CEntity* ent)
{
IFuelSupply* fs;
int fuel_rcvd;
fs = dynamic_cast<IFuelSupply*>(ent);
if(fs != NULL)
{
// Entity has the IFuelSupply interface
// Attempt to get 50 units of fuel
fuel_rcvd = fs->ReFuel(50);
// fuel_rcvd now hold the amount of fuel you managed
// to get from the FuelSupply
// add fuel_recd to fuel tank or whatever
// etc
}
};
};
This example may be contrived, but if you had a mobile FuelTanker entity and a static
Hope this helps,
Archimage
(Ann-Marie Ratcliffe)
[edited by - Archimage on May 12, 2004 5:22:02 AM]
Extending the idea above slightly, if you had quite a few types of possible supply (IFuelSupply, IAmmoSupply, IMissileSupply, IWidgetSupply, IDoodadSupply) you could create a base ''interface'' ISupply , which the other interfaces derived from (virtually, to prevent problems if you had an entity which was several I(x)Supply''s eg fuel and ammo). Then you would first check to see if you could dynamic_cast to ISupply*. If you can''t, there is no point in testing for all the specific I(x)Supply''s.
Archimage
(Ann-Marie Ratcliffe)
Archimage
(Ann-Marie Ratcliffe)
ArchImage, thanks a ton. That gave me a ton of ideas! One last little issue that I HAD was with scripting...
create a new pointer of a derived class, set the member values,
add it to the scene graph and your done. Im not really going to use scripting for ai (very slightly) what im going to use it for is to simply add objects to the world and set there properties basically. Im probably going to use lua btw.
That does however bring up a issue later on... if I did want to script ai routines in lua, how would I do type casting...well, I know what I will do, but just to show you where im going with all of this
Thanks a ton!
Now, explain to me how im going to design the octree system with the scene graph... joking
~Jason
create a new pointer of a derived class, set the member values,
add it to the scene graph and your done. Im not really going to use scripting for ai (very slightly) what im going to use it for is to simply add objects to the world and set there properties basically. Im probably going to use lua btw.
That does however bring up a issue later on... if I did want to script ai routines in lua, how would I do type casting...well, I know what I will do, but just to show you where im going with all of this
Thanks a ton!
Now, explain to me how im going to design the octree system with the scene graph... joking
~Jason
Interfaces give behaviors to unrelated types that don''t belong to any hierarchies, they can give multiple "roles" to an object that are required to perform, there are two ways in doing this by dynamic/static polymorphism. Like some people of shown already doing it by dynamic polymorphism is achieved in C++ with ABC classes. The other way which i prefer to try and do as much as possiable is using static polymorphism, which is achieved in C++ with templates and generic programming e.g.
Which is the exact same as doing it by ABC classes but no runtime overhead.
#include <cstdlib>
#include <iostream>
template<class T> //T must support the draw operation
void render(const T& obj) {
obj.draw();
}
class a {
public:
void draw() const {
std::cout << "CLASS A\n";
}
};
class b {
public:
void draw() const {
std::cout << "CLASS B\n";
}
};
int main() {
a _a;
b _b;
render(_a);
render(_b);
std::system("pause");
return EXIT_SUCCESS;
}
Which is the exact same as doing it by ABC classes but no runtime overhead.
quote:
Original post by snk_kid
Which is the exact same as doing it by ABC classes but no runtime overhead.
Although I see where you are coming from with the static polymorphism, it does put one major restriction on the system - that you (or more precisely the compiler) know exactly what derived-class each entity is at compile time.
The situation we've been discussing above, especially when applied to dynamically creating the entities via a run-time script, voids this possibility.
Archimage
(Ann-Marie Ratcliffe)
[edited by - Archimage on May 12, 2004 6:14:27 AM]
quote:
Original post by Archimagequote:
Original post by snk_kid
Which is the exact same as doing it by ABC classes but no runtime overhead.
Although I see where you are coming from with the static polymorphism, it does put one major restriction on the system - that you (or more precisely the compiler) know exactly what derived-class each entity is at compile time.
The situation we''ve been discussing above, especially when applied to dynamically creating the entities via a run-time script, voids this possibility.
Archimage
(Ann-Marie Ratcliffe)
[edited by - Archimage on May 12, 2004 6:14:27 AM]
I should have read the discussion properly, i just thought i would mention it because people tend to go overboard with dynamic polymorphism when most of the time it isn''t needed and most things tend to be known.
quote:
Original post by snk_kid
I should have read the discussion properly, i just thought i would mention it because people tend to go overboard with dynamic polymorphism when most of the time it isn''t needed and most things tend to be known.
I didn''t mean to snap at you (if I did, apologies). Actually what you put forward can be very useful (I''ve used it to great effect in the past), it just didn''t seem relevant to the topic in hand. Having said that, its always a good thing to let people know about
Yours,
Archimage
(Ann-Marie Ratcliffe)
quote:
Original post by no one
I just did a test on this and it seems that by casting a entity pointer to some derived class, you can access the memebers and get there correct values...
Heres a small program I just made to test this...
#include <iostream>
#include <string>
using namespace std;
enum rtti
{
edefault,
ederived1,
ederived2,
};
class CBase
{
public:
CBase()
{
SetType(edefault);
mpInt = 1;
}
~CBase(){}
int mpInt;
rtti mpType;
virtual void SetType(rtti type)
{
mpType=type;
}
virtual void Disp(void)=0;
};
class CDerived1 : public CBase
{
public:
CDerived1()
{
SetType(ederived1);
}
~CDerived1(){}
float mpFloat;
void Disp()
{
cout<<"derived attribute "<<mpInt<<endl
<<"derived1 class's attribute "<<mpFloat<<endl;
}
};
class CDerived2 : public CBase
{
public:
CDerived2()
{
SetType(ederived2);
mpChar = new char;
}
~CDerived2(){delete mpChar;}
char *mpChar;
void Disp()
{
cout<<"derived attribute "<<mpInt<<endl
<<"derived2 class's attribute "<<mpChar<<endl;
}
};
CBase *g_list[3];
void Init()
{
CDerived1 *d1 = new CDerived1;
CDerived2 *d2 = new CDerived2;
CDerived2 *d3 = new CDerived2;
d1->mpFloat = (float)rand();
d2->mpChar = "whats up";
d3->mpChar = "test";
g_list[0] = d1;
g_list[1] = d2;
g_list[2] = d3;
}
int main(void)
{
Init();
g_list[0]->Disp();
g_list[1]->Disp();
CDerived1 *d;
if(g_list[0]->mpType == ederived1)
{
d = (CDerived1*) g_list[0];
d->mpFloat = 10.16;
}
g_list[0] = d;
g_list[0]->Disp();
float newvalue;
CDerived1 *d2;
if(g_list[0]->mpType == ederived1)
{
d2 = (CDerived1*) g_list[0];
newvalue = d2->mpFloat;
}
g_list[0] = d2;
cout<<"new value "<<newvalue<<endl<<endl;
char *name;
CDerived2 *d3;
if(g_list[2]->mpType == ederived2)
{
d3 = (CDerived2*)g_list[2];
name = d3->mpChar;
}
g_list[2] = d3;
cout<<name<<endl;
for(int i = 0; i < 2; i++)
delete g_list[i];
return 0;
}
I feel thats the answer. Now, ill just have to come up with a clean way of doing all of that nasty code.
Thanks.
Now i had better look at this discussion ;-) wouldn't it better to have type id stuff as class members instead of instance members if you get what i mean. Keep much of it the same but have data members as static constants
[edited by - snk_kid on May 12, 2004 8:47:05 AM]
quote:
Original post by snk_kid
Now i had better look at this discussion ;-) wouldn't it better to have type ID stuff as class members instead of instance members if you get what i mean
Oops, you are right - now who has to look shame-faced about not taking a close look at the discussion <blush>. You shouldn't SetType the class, rather the derived classes have their own 'GetType', which is always the same for that class. Luckily we've moved on from that, I think/hope.
Archimage
(Ann-Marie Ratcliffe)
PS Thanks for not rubbing it in
[edited by - Archimage on May 12, 2004 8:54:03 AM]
Talking about scene graphs, i would like to hear the views from people about something i''ve had on my mind recently. Traditional scene graph are usually implementated with thew design patterns put together quite elegantly but can get quite complicated, the graph itself is in essence of a composite pattern it seems to me.
I was just wondering weather how would alternate ways compare like instead of building a tree structure within the type hierarchy together have a generic tree structure and a simplified type hierarchy. I''ve even read one suggesting of getting rid of the tree altogether and use an std::set, set of objects in the scene so to say.
I was just wondering weather how would alternate ways compare like instead of building a tree structure within the type hierarchy together have a generic tree structure and a simplified type hierarchy. I''ve even read one suggesting of getting rid of the tree altogether and use an std::set, set of objects in the scene so to say.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement