Skip to main content
gamedev.net
🔒 Locked

Inheritance

Started by alirakiyan Aug 30, 2011 at 9:02 AM 11 replies 2.2k views
Original Post
alirakiyan
alirakiyan
hi
consider we have two similar classes which work on numbers and return some values.
ClassA and ClassB
ClassA works on floats and returns the abs of the input value.
ClassB works on DWORD values and returns the sum of input values.
both have the function Work() which acts on input and returns the result.

we need a class ClassC which merges abilities of ClassA and ClassB.
and then just create an instance of ClassC , give the input and get the return value.
ClassC should have a function Work() which acts on input and returns the result.

could any one tell me how to do that please?
could this problem be solved by Inheritance or polymorphism?
Wooh
Wooh
If you want to do this the Work function of both ClassA and ClassB must take the same types of arguments and have the same return type. It sounds like you want to use different types depending on the base class. I don't see how that could work.
alirakiyan
alirakiyan
there are many kinds of similar objects in a game.
Door , Tree , and Car.
all of them must be Loaded !!
Door and Car need animation and must be updated per frame but Tree doesn't.
now consider that we have 100 files of three types Tree ,Car ,and Door
a common class is needed to load the object which just gets the file name as input and create the object (CCar for cars , CTree for Tree and CDoor for Door.)
once the object is loaded , we know what type it is and we can create it.
then a common function is needed to Draw all the 100 objects.
this common function should not know any thing about the object. it should just tell the object to draw itself.


how can I do that?
can you help me?
rip-off
rip-off
Have a container of Cars, a container of Trees and a container Doors. Common functionality can be factored into separate classes - e.g. Cars and Doors have Animations, but Trees don't. They all might have PhysicalObjects, which describe position, rotation, collision dimensions etc. Animations have a set of Textures/Meshes, a Tree might only have one.

Inheritance is for IS-A relationships. None of these objects are necessarily logically related. You could design them so that they are, but such a design often couples classes that are only vaguely related.

For your first post, it seems like you just want a few functions with the same signature, not necessarily requiring any state at all. As such, I wouldn't use classes unless I had an implementation that required state.
Dragonsoulj
Dragonsoulj
Perhaps the way you want to go is Templates, not Inheritance.
kuramayoko10
kuramayoko10
I think you want a simpler approach, and still be using inheritance and polymorphism:

Here is a simple sample code.

#include <vector>

using namespace std;

class GameObject
{
protected:
int x;
int y;

public:
GameObject(int x, int y)
{
this->x = x;
this->y = y;
}
~GameObject()
{

}

virtual void LoadObject() { }
virtual void Update() { }
virtual void Draw() { }

};

/*Inheritance*/
class Car : public GameObject
{
private:
int speed;

public:
/*Inheritance*/
Car(int x, int y, int speed) : GameObject(x, y)
{
this->speed = speed;
}
~Car()
{

}

/*Methods overwritten*/
void LoadObject()
{
//Do specific load for car here
}

void Update()
{
//Do specific update for car here
printf("Updated car!\n");
}

void Draw()
{
//Do specific drawing for car here
}
};

class Tree : public GameObject
{
private:
int height;

public:
Tree(int x, int y, int height) : GameObject(x, y)
{
this->height = height;
}
~Tree()
{

}

void LoadObject()
{
//Do specific load for tree here
}

void Update()
{
//Do specific update for tree here
printf("Updated tree!\n");
}

void Draw()
{
//Do specific drawing for tree here
}
};

int main (int argc, char * const argv[])
{
vector<GameObject*> *world = new vector<GameObject*>;
vector<GameObject*>::iterator iter;

/*Add some objects to our world*/
world->push_back(new Car(10, 10, 30));
world->push_back(new Car(30, 10, 50));
world->push_back(new Tree(20, 10, 90));

/*Iterate through them to do update/draw/etc*/
iter = world->begin();
while(iter != world->end())
{
GameObject *selected = *iter;

/*Polymorphism here*/
selected->Update();
selected->Draw();

iter++;
}

return 0;
}


This code shows a simple polymorphism example in which you can use the superclass "GameObject" to activate common functions on the child. Those commom functions have to have only the same signature, the contents are unique.

Hope it helps,
Programming is an art. Game programming is a masterpiece!
GorbGorb
GorbGorb
I don't think that approach will help. Your objects must eventually communicate, and to do so, they need more information about other objects. Your code will lead to dynamic casts all over the place, or alternatively a giant blob GameObject that implements function like 'is_tree_and_choppable'. Google component based desing or entity system, and you'll find better explanations of the issue.
@topic:
I would suggest your GameObjects that need drawing implement an interface, something like this:


class Drawable
{
public:
virtual void draw( Canvas &RenderTo ) = 0;
};

class Car
: public Drawable ,
public Collidable ,
...
{
public:
void draw( Canvas & )
{
//draw a car
}
};



You could then store pointers to all Drawables in a std::vector and iterate over them if you want to draw.
kuramayoko10
kuramayoko10

I don't think that approach will help. Your objects must eventually communicate, and to do so, they need more information about other objects. Your code will lead to dynamic casts all over the place, or alternatively a giant blob GameObject that implements function like 'is_tree_and_choppable'. Google component based desing or entity system, and you'll find better explanations of the issue.


It was not the point of my example the real structure for his game. It was just an example of how inheritance and polymorphism work.
But, thank you for complementing my post including Interface. It is always good to draw a diagram of the objects in the game to stipulate relationships about them, and have in mind:
  • Inheritance class A : public B means A is B
  • Interface class A implements B means A does B (normally B is identified as an adverb)
Programming is an art. Game programming is a masterpiece!
Codarki
Codarki

could this problem be solved by Inheritance or polymorphism?

Yes it could, but I dont think it should.


hi
consider we have two similar classes which work on numbers and return some values.
ClassA and ClassB
ClassA works on floats and returns the abs of the input value.
ClassB works on DWORD values and returns the sum of input values.
both have the function Work() which acts on input and returns the result.

we need a class ClassC which merges abilities of ClassA and ClassB.
and then just create an instance of ClassC , give the input and get the return value.
ClassC should have a function Work() which acts on input and returns the result.

could any one tell me how to do that please?


class ClassC
{
public:
void Work()
{
class_a.Work();
class_b.Work();
}

private:
ClassA class_a;
ClassB class_b;
};
_the_phantom_
_the_phantom_

@topic:
I would suggest your GameObjects that need drawing implement an interface, something like this:


And I would suggest that level of abstraction will lead to all manner of problems and bloated inhertiance trees.

What happens when you need an object which is a game entity, drawable, collidable, plays sounds and takes input, be it player driver or AI?

class Car : public entity, public drawable, public collidable, public soundsource, public input {} ?

Instead consider; a Car HAS-A graphical representation in the world, HAS-A collidable represenation in the world, HAS-A controller for input, HAS-A sound source it can use to play things.

Suddenly your Car is now being just a composite Car object and doing only Car things, not having to worry about being everything else, is more flexible, decoupled AND no longer violates the single responcibilty principle by being just a car rather than some massive object which tries to do everything...


You could then store pointers to all Drawables in a std::vector and iterate over them if you want to draw.
[/quote]

Composition grants you the same thing; your object holds a reference to its representation in the world, and your backend renderer can also hold a reference in order to deal with drawing it.
GorbGorb
GorbGorb

What happens when you need an object which is a game entity, drawable, collidable, plays sounds and takes input, be it player driver or AI?

class Car : public entity, public drawable, public collidable, public soundsource, public input {} ?

Instead consider; a Car HAS-A graphical representation in the world, HAS-A collidable represenation in the world, HAS-A controller for input, HAS-A sound source it can use to play things.



I actually use your system, but I don't see a big difference between the interfaces and the components approach. With components, your car would look like this:


class Car
: public Entity
{
DrawComponent draw_comp;
CollidableComponent collid_comp;
SoundComponent sound_comp;
InputComponent input_comp;
};


Why is this better? You still have to do logic in car, e.g. tell the CollidableComponent how big your car is.
_the_phantom_
_the_phantom_
Removing the 'car' from a web like hierarchy is in itself a good thing; it makes modification of the Car easier and doesn't bog down subtypes with interfaces which might or might not change.

For example if you had a Car which was AI controlled and one which was Human controlled with the inhertiance system you'd have to get to a point where you can say a Car IS-A player controlled and a Car IS-A AI controlled entity, which could mean you end up with two classes PlayerCar and AICar both of which derive from Car... and all that other stuff.. to do the work. In a component system you have a 'controller' class which can be AI or Human doesn't matter, it is just given to a Car object to use for input in some manner.

Removing things like 'renderable' simply make code better. It means you can simplify your 'renderable' to do just that - hold information to render. Your Car no longer has to car about how to render itself, it just maintains a handle to something which contains the data/info on what is rendered. This object can store this information in a manner which is optimal for the renderer both in data AND in code as instead of having to do loads of 'virtual' dispatches in your central rendering code to draw cars, trees, cows etc you have one single 'renderable' type which is tailored for the subsystem (most likely DX or OpenGL buffer handles and state information).

The point is your classes become 'smaller' and more focused, they can do their job better, you can store components in containers per-type to remove overhead of virtual look up and dispatch (not saying "don't use virtuals", but this gives a more focused design which does away with the overhead) and you can centralise code and data to the systems making them generally more friendly for the CPU to deal with.

Or for a more direct example; you have 20 different entities which all need to be rendered, each has their own virtual 'render' function. You have a list of 300 entities to render, held by pointer to "IRenderable", which are in a random 'real type' order. This means that in order to render that list you risk, in a worst case, suffering 20*300 cache misses on the vtable alone, never mind all the associated data (and compared to cache system memory is sloooooooooow). By contrast a list of 300 'Renderable' objects held by-value and in contiguous memory is going to suffer little to no cache misses as you stream through the data, in order, working with the pre-fetcher of the CPU, removing much of the data miss overheads.

Does the inhertiance system 'work'? Well, yes, for a given value of 'work'. Does a better system exist? Yes, for most given values of 'better'.

And yes, you still have to give your components some data, but that would be done at construction/initialisation time. At which point they own the data belongs to them and is kept in a nice contigous chunk somewhere, all CPU friendly ready to be accessed

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.