Archived

This topic is now archived and is closed to further replies.

paulcoz

Class / struct question for objects

Recommended Posts

I need to create some sort of structure or class to store all of the objects in a map in memory. I am thinking of reading in all of the world data (walls, cubes, doors etc..) and putting this into an array, but I would like some advice on the organisation. If I were to create an 'object' array and store everything in this one class then that would be fine, except that you might be using a lot of memory for parameters which are empty like this: object( objecttype wallsize doorangle waterspeed openspeed ) If I were to create a different class for each object then I fear I might have trouble when I come to doing stuff like a z sort to optimise the drawing order of objects - because the objects parameters are spread out over a number of separate arrays. Example: wall{ wallsize colour } door( openspeed doorangle rotatetrue } Any ideas on how to organise this? Paulcoz. Edited by - paulcoz on 4/9/00 9:29:26 PM

Share this post


Link to post
Share on other sites
Well, I would suggesting creating a base Object class and then inhereting your specific objects from it.


class Object {
int z_order;
};

class Door : public Object {
int doorangle;
};

class Wall : public Object {
int wallsize;
};

Then you can use a Object * array to do your z sort, but still maintain a Wall array for your walls, etc.

Share this post


Link to post
Share on other sites
Curses, SiCrane beat me to it (only by a few hours)

I totally agree with him (sorry if you''re a her, SiCrane ). By having a base class Object, and deriving other object classes from it, you can have an array of pointers to Object objects, and then there is no need to have separate arrays for each object type (unless this makes it easier to access and change data members, in which case you can have separate arrays for each object type with pointers to the actual objects referenced by the array of pointers to Object objects.... shit that''s confusing )

Yes, this is the way to go... ah, polymorphism, what joys you hold.

thankyoubye

-------------
squirrels are a remarkable source of protein...

Share this post


Link to post
Share on other sites
It sounds like you both know what I am trying to do, but Bad Monkey has confused me (no offense Monkey Man ).

How do you distinguish between (and refer to) values stored in the base ''object'' class and those in the ''sub-object'' class when they are combined in one single entity?

Is having an array of one of the sub-classes really that different to what I put in my second example? Isn''t that still keeping the different objects split up into different arrays?

I need your help again,
Paulcoz.

Share this post


Link to post
Share on other sites
quote:
Original post by paulcoz

How do you distinguish between (and refer to) values stored in the base ''object'' class and those in the ''sub-object'' class when they are combined in one single entity?


Why would you need to? If you make sure none of the variable names are the same, then you just refer to them by name as you would with any other struct or class.

quote:
Is having an array of one of the sub-classes really that different to what I put in my second example? Isn''t that still keeping the different objects split up into different arrays?


I thought it was suggested to keep them all in the same array? Therefore, if you implement stuff like z-order in the base class, you can use that array cos you won''t need to know whether it is a door or a wall or a banana just to know its z-order. If there is something else where you need to know the type, try looking up RTTI or perhaps implementing a virtual method called GetType() in the base class which you override in each of the derived classes to return a variable saying what they actually are.

Share this post


Link to post
Share on other sites
Perhaps telling you what I am trying to do would be helpful:

(1) read in all the map data from a file, putting all of the world objects into memory either:
(a) in a 'object' array which can contain any type of object, or
(b) in a special array for that specific type of object eg. cube, wall, door etc.. (for the memory reasons I mentioned previously).
Which one I choose depends on which structure makes step (2) the easiest.

(2) Draw all of the objects in the world.
(a) First check the order in which the objects should be drawn (front to back to minimise redraws) - this is done by checking the z-values in camera space which requires that you have access to an object's vertices.
(b) Check the type of object in the appropriate object array and run the correct code eg. for a cube run the cube-drawing code, for a sphere run the sphere-drawing code.

In 2(a) you obviously need access to the object's data, and whether or not the objects are in a big non-specific array or otherwise is irrelevent while you are reading the data in.

When you are telling the program what order to draw in, however, and objects are split over multiple arrays things becomes messy because you have to draw one object from one array, say wall[0] then one from the array door[3] etc..
Or, if the objects are read into a base object entity, won't that mean there are a whole lot of empty wall classes, door classes etc.. taking up memory like I mentioned before?

Can I apply what SiCrane and Bad Monkey suggested to this situation? I don't understand how these sub-classes work, sorry.

Paulcoz.

Edited by - paulcoz on 4/11/00 6:26:12 AM

Share this post


Link to post
Share on other sites
Ok, let''s say I have the class Object like so:

class Object {
public:
virtual void Render(Surface *) = 0;
virtual int GetZOrder(void) = 0;
};

Because the two object methods are pure virtual, an object of class Object can never be instantiated. However, I define some subclasses:

class Door : public Object {
public:
virtual void Render(Surface *) {}
virtual int GetZOrder(void) { return 0; }
};
class Wall : public Object {
public:
virtual void Render(Surface *) {}
virtual int GetZOrder(void) { return 0; }
};

(Obviously replace Render and GetZOrder with functions that work.) Now, I *can* create instances of Door and Wall, because the define the pure virtual methods in class Object. So when I read in the map data, I''ll create Doors and Walls. Because they both are inherited from Object, I can cast Door *''s and Wall *''s to Object *''s without a problem, and stick them in the same array. Something like

Object * objects[MAX_OBJECTS];
int ptr = 0;
while (still map data) {
if (next object is a door) {
Door * temp = new Door();
objects[ptr] = temp;
ptr++;
} else if (next object is a wall) {
Wall * temp = new Wall();
objects[ptr] = temp;
ptr++;
}
}

New I can loop through my objects array and sort by Z-order, then loop through and call the Render methods. Because they are virtual functions, even though I''m using Object *''s to reference the objects, the correct Door or Wall method will be called for Render() and GetZOrder().

Did that clear things up?

Share this post


Link to post
Share on other sites
Bravo SiCrane

I''m glad you came up with that awesome explaination... I was about to go at it, but when I got to your post, joy filled my heart. My head''s a real mess sometimes, but I know what I''m getting at

This guy deseves a medal or something... that was better than the crap they tried to teach me at uni last year. Is the path clear now Paulcoz?

-------------
squirrels are a remarkable source of protein...

Share this post


Link to post
Share on other sites
So instead of having an array of sequential objects, you have an array of sequential pointers to jumbled objects, right?

Two questions:

(1) SiCrane said "an object of class Object can never be instantiated" which I interpreted as *you cannot use the objects based on the Object class, you can only create other classes based on that class and use objects based on those*, then he used the following line:

Object * objects[MAX_OBJECTS];

Isn''t the array ''objects'' based on the Object class and therefore contradicting what he said? I hope it doesn''t sound like I am criticising, I am just trying to comprehend.

(2) Can the sub-classes and base-classes have different function names/purposes or do these virtual functions have to be exactly the same?

I obviously need to read some more about classes because I thought they were just the C++ version of the structure in C. Looks like they are more involved (I''ve only been using C++ for about five months!!!).

Thanks,
Paulcoz.

Share this post


Link to post
Share on other sites
No, he's not actually contradicting himself, though it could seem like that if you're not familiar with inheritance and polymorphism. Basically, you can declare an (Object *), since that's just a pointer (= memory address) for an Object, but it doesn't really create an Object. You can not make an Object (either by declaring one, or using "new Object" with an (Object *)). Since you've left parts of Object undefined, making an actual instance of Object will fail to work. But, an (Object *) can be set up to point at anything derived from Object, which is what you'll store in the array he mentioned.

Yes, I would suggest finding a decent C++/OOP book to cover some of this material. There is a tremendous amount of stuff to cover, and some of it can be very difficult/confusing the first few (several? many?) times through.

You can add new functions to derived classes, but virtual functions should be called in the same way. The reason is that virtual functions are "smart". If you have a (Object *), it can point to anything derived from Object. And if you use it to call any virtual functions, it automatically figures out what type of Object it's pointing to, then calls that version of the function for you. This is how, like SiCrane said, you could stick all of your objects in an array of (Object *), then run through that array calling the render function. As long as each type of thing (wall, enemy, ...) is derived from Object, each one can provide its own render() function, and when you call ObjectArray[ n ]->render(), you don't even care what type of object it is, it will use the correct version for that object.

Hope this helps a little more, but I realize this is rather confusing. It takes a while to adjust to OOP from procedural programming sometimes, and getting a good book will definitely help.

-Brian

Edited by - osmanb on 4/11/00 11:44:28 PM

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Hi Paulcoz,

Sorry about my previous attempt at explaining... it was a bit all over the place

What SiCrane meant about being unable to instantiate and object of class Object is that using the calls:



Object *thing; //this line is OK

thing = new Object();
thing->Render();



... would be invalid, as it is an abstract class (defined only for the purpose of unifying some subclasses). It doesn''t make sense that an object of this type can, for example, draw itself. What does an Object look like?

You can, if I remember correctly, have a pointer to this abstract class, you just can''t create an object of that class.

Now, this is useful in that you can have an array of pointers to ''objects'' of this abstract class, but assign objects of subclasses to these pointers:
NOTE: I''m using parentheses for array subscripts, as this message board uses the square brackets for stuff.



Object *things(3);
thing(0) = new Door();
thing(1) = new Wall();
thing(0)->Render();
thing(1)->Render();



... this is valid, because Door and Wall are not abstract classes and are subclasses of Object. The beauty of this is that you can iterate through your array calling the Render() method for each object, and polymorphism/function overloading takes care of determining what type of object (subclass) it is dealing with, and calls the appropriate function code for that class.

This leads me to answer the next part of your question. Each subclass of Object (staying with the established example) can have any methods they want, but they must implement all virtual methods defined in the superclass (Object). The way they implement it is, however, entirely up to you. For example, to implement the Render() method for a Door object, you write your drawing code to render a door. Now obviously it takes different code to render a wall (OK, maybe not that different), so you would write a different implementation of the Render() method for the Wall class:



class Door : public Object
{
public:
Door();
void Render();
};


void Door::Render()
{
drawPanel;
drawKnob;
}


class Wall : public Object
{
public:
Wall();
void Render();
};


void Wall::Render()
{
drawBricks;
}



Do you see what I mean. Each class has its own way of providing an implementation for the virtual method/s of the superclass (Object) that is relevant to an object of that particular class .

Sorry if I just repeated what you already knew. Hope that was a bit more helpful than my last try



-------------
squirrels are a remarkable source of protein...

Share this post


Link to post
Share on other sites
Thanks a lot folks, I know I should read more about these concepts before posting, but then again I didn''t know that classes could do that sort of thing when I posted. I have been ''clinging'' to my procedural programming skills for as long as I can, but it looks like now is the time for me to learn something new - those virtual functions look great.

Your help has been extremely useful & Bad Monkey your first explanation wasn''t bad, I was just that I had a very limited knowledge of the subject, thankyou.

Paulcoz.

Share this post


Link to post
Share on other sites