Sign in to follow this  
aaron_ds

Abstracting and decoupling filedata from in-game representation

Recommended Posts

aaron_ds    486
I'd like to rework some of my file loading routines into something that is more modular, dynamic and extensible. In the beginning, I concentrated the file loading into one method in the gamestate. GAMESTATE::Load(char* filename). Next I decided that it was better to recursively load the world. Each object would load itself and its children which would load their children, etc. Now, I realize that having the objects load themselves is a generally bad idea as it spreads the loading code throughout the project making it unwieldy and fragmented. I'd like to have some kind of file loading class which can be easily extended and isn't so coupled to how objects are portrayed in game. I must mention that I'm using 3dsmax and Maxscript to export data into a custom binary format. The format isn't set in stone and can be easily changed to accommodate the importer. Any sites or suggestions on how to move away from a simple file loader into something that has more finesse? How do you load your level data? Thanks in advance! Cheers! [Edited by - aaron_ds on October 1, 2004 1:24:34 AM]

Share this post


Link to post
Share on other sites
umbrae    308
You are right when you say that spreading the code around into the actual objects is fragmented.

I haven't really though about it much, but this is my first impression:

Construct a new class hierarchy, identical in design to your world hierarchy. That way you get the best of both worlds

extensible - it would be easy to add more classes to load new objects
modular - each loading routine is in one class and each class needs to only know about it's alter-ego - the class that it has to create from the data given.

Just an idea until I think about something better.

Share this post


Link to post
Share on other sites
vovansim    336
Hi,

Quote:
Original post by umbrae
Construct a new class hierarchy, identical in design to your world hierarchy. That way you get the best of both worlds


That's not really a ood solution, because suppose your game data hierarchy changes. Then you have to change it, and the loading routines, which really doesn't make the method any better than it was in the original design.

aaron_ds, this is kind of a widespread problem. I don't really know a really good solution which would work for any data, and you kinda need to tailor the solution to your particular data format. For instance, if you were loading from XML files, then there is a fairly simple way to do it, because then you can create essentially a placeholder associative array with the data from it, where the keys would correspond to tag names, and values would correspond to the data in the tags. Then, you would pass this on to some factory and it would process it according to what object kind is being created.

With binary files, which is what you are using, then it becomes a little harder. Of course, you could always write the data in key-datatype-value triplets or something, and then when you read it in, you do the same thing as in the above example.

Why would this be good?

1. Because you only have *one* file loading class, and when your data changes, everything low-level works automagically.
2. As a consequence of 1, you can switch data representations. Change to XML, another custom ASCII format, or even a database, and the data layer wouldn't care.

Why would this be bad?

1. You still need to write the code to process this placeholder object in the data object, and you still need to change it if your data object changes.
2. ... Ehh, there was something else I meant to mention, but I forget.

Vovan

Share this post


Link to post
Share on other sites
aaron_ds    486
I think what I need is a generic heirarchy into which I can load the file data (since historically level data is arranged like this and it could even be used ot represent linear data)

something like

template<typename T1, typename T2, typename T3,...>
class chunk{
public:
friend ifstream& operator>>(ifstream&, chunk&);
list<T1>* getlist1(void);
list<T2>* getlist2(void);


private:
char* type;
list<chunk> children; //dont want children to have to be same type as parent..blah
...//some kinda heterogenous container,
//or container of heterogenous lists.
list<T1> list1;
list<T2> list2;
};




(Yeah, I know this isn't compilable, just an example of what I'm looking for)


I'm looking for some kind of data structure which is a heterogenous tree that can contain a wide variety of POD. each node/chunk could have a simple string describing how the factory should interpert it. eg: polygon, light, material.
each node would have to be able to hold any number of lists of different types and a list of its children.

Is this even possible?

Share this post


Link to post
Share on other sites
Nemesis2k2    1045
I've done some thinking about this for my own project, and I've come to the conclusion that the system where each object loads itself is the best one. First of all, think of this problem the other way around. Right now, you're thinking of an overall "load file" process, and you see that being comprimised by splitting it into parts over a whole bunch of classes. That's a functional way of looking at it, not an OO way of looking at it. In an OO solution, you focus on the objects, not the processes. Each object should be responsible for loading its own data, and only that object should know how to load it. If you pass that job over to a seperate entity in your program, you're going to encounter problems.

First of all, if a seperate entity extracts all the data for that object from the file, then passes it along to the object once it's all been grouped nicely, you've just effectively created a bind between the internal data members of that object and the loading entity. If you change some of the data members of the object, chances are you'll have to change the way it is represented in the file, and that means you'll have to make modifications both inside and outside your class. If the object was responsible for loading itself, no changes would be required outside that class.

Secondly, how are you going to pass that data along to the object? On paper, all of those data members should be private, so there should be no way for that seperate entity to pass them along directly. You really only have two options I can see, both equally messy and flawed. You could have a massive constructor or member function that has a billion parameters, that you pass each data element to, or you could make this seperate entity a friend, and allow it to load the data in directly. Both are a big no no.

Share this post


Link to post
Share on other sites
JohnBolton    1372
The solution I have come up with is to have two classes: Object and ObjectLoader. Basically, the ObjectLoader creates an Object using data loaded from a file simply by loading the data and then passing it to the Object's constructor. Here's an example:

class Texture
{
Texture( /* Initialization data */ );
...
};

class TextureLoader
{
Texture * LoadFromStream( iostream & stream )
{
// Load data from the stream
return new Texture( /* data loaded from the stream*/ )
}

Texture * LoadFromBMP( string const & name )
{
// Open file
// Load BMP data
// Close file
return new Texture( /* data loaded from BMP */ )
}

Texture * LoadFromTGA( string const & name )
{
// Open file
// Load PNG data
// Close file
return new Texture( /* data loaded from TGA */ )
}
Texture * LoadFromPNG( string const & name )
{
// Open file
// Load PNG data
// Close file
return new Texture( /* data loaded from PNG */ )
}

...
}





In this case, the Texture class doesn't have to know about all the different file formats and the TextureLoader class doesn't have to know about the internals of a Texture.

Share this post


Link to post
Share on other sites
nickmerritt    122
I use an interesting way i picked up off an Enginuity article. Each object is responsible for know what data, it needs, but not how it's stored or loaded.
Ie:
each object has a ISerialize(ISerializer* target)
{
target->IO(m_someInterger, "This is a description of the data.");
}
then i can save to file, or input from an editor or anything works well!


Nick

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this