Game Data Management Problem (BlackSky)

Started by
5 comments, last by Extrarius 19 years, 3 months ago
Ok I need some help. I'm currently making the game Black Sky. I'm working on adding file loading, which will allow me to keep variables outside the game, and work on the game balancing without re-compiling. But i'm running into coding problems when it comes to how I'm going to manage my data. Currently, I was going to/am doing the following: Game loads all ship components (engines, sensors, reactors, weapon designs, etc) Then it loads all the spaceships. Now, what it needs to do is have no limit in the design of how many components of each kind, a ship can have. Example. Lets say I have a spaceship, and it has 4 engines, 2 reactors, 25 turrets, 3 sensors. The 20 of the 25 turrets are twin firing blah blah. The other 5 are quad rail guns. So after I set up this ship externally to the game, I have to load it into the game. The problem is that there can be no limit to how many ships, components can exist (within reason, im not hardcoding anything in) This means my GameData template has to be able to do dynamic templates. So when I create a ship on my screen, it looks up which ship I want to create, then copies the information it needs into my ship vector. Now my question is what would be the simplist and easiest way to do this system. My other problem is how do i link everything together. For instance, I could have 10 different ships on the screen, each ship would have it;s own turrets, engines, internal system etc. For example, I have to cycle through everyship everyframe and update it. This includes, cycling through all the turrets of each ship, if the ship has any, and update those. Right now im using structures with vectors. The hard part it about it all, is each system is pretty unique. I can have a ship, that has 10 DIFFERENT turrets on it. Each turret has it's own firing rate, turn rate, and shoots a different weapon. I'm using raw pointer structures inside of vectors. Basicly the way I handle it is like this, I have a vector of entities, that store x location, y location, its current heading and so on. So really it's a vector of a structure. Pointer structures. Inside this entity structure, I have another structure, Called Ship. Ship contains all the information about the ship it's self (how fast it can go, its armor, all that stuff).Inside the Ship structure as well, I have an individual vector for each component. Turrets, engines, sensors, reactors, etc. So basicly if I wanted to access something, I would have to go like shipent[index]->Ships->Turret[x]->heading=0; For example lets say I have 10 ships, each ship has 2 turrets, each turret shoots 2 weapons in a cycle fire. My first loop goes through the ships (movement code, ai) the first nested loop goes through the turrets (it updates where its aiming at) if the turret has more than 1 weapon, I have to loop # of weapons on the turret I see if the weapon is ready to fire, then I spawn in a laser particle, based on where the turret is. When the ship dies, I have to manually delete all the pointers (turrets, weapons on turrets). Then deletee the entity its self. then erase the index the ship is in in the vector. Here's another example of how I do things. psuedo code

struct weapons
{
  whatever weapons have
}

struct turrets
{
  whatever turrets do
  vector<weapons*> Weapon;
}

struct ships
{
 whatver ships do
 vector<weapons*> Weapon;     //ships can have weapons as well as turrets
 vector<turrets*> Turret;
}

struct entity
{
 whatever entities have
 struct ships Ship;
}

weapons *Weapons;
turrets *Turrets;
entity *Ent;
vector<Ent*> ships

This has been fine and all, but now I have to do a new system to keep the templates for everything.

struct Gamedata
{
        vector<struct engine*> Engines;
	vector<struct turret*> Turrets;
	vector<struct ship*> Ship;
        vector

};

Anyways you get the idea. Gamedata will store all the info by using vectors At the moment I was trying to do something like ship[x]->Ship=GameData.Ship[shipid] This way I could just copy structure to structure. In other words, it would copy the entire ship template, into the entity.Ship structure. When I create a ship, I can copy the entire "image" or template of the ship into the ship, meaning it becomes the template. The biggest problem is GameData structure in order to keep everything in the heap I have to pass pointers into the vectors. vector<struct ship*> Ship; because if I did that, I would have to go ship *Ship; And that is giving me compile errors. I have to go struct ship *Ship; which im not sure why. I think it has problem with doing pointer structures that have vectors in them. Reason why I have to make ship into a pointer is because. Ship= new ship; Ship->shipstuff=x; ... ... GameData.Push_back(Ship) this way I can have as many ships as I want (within reason again) I could keep the ship struct not a pointer in the GameData structure, and just pass full structure through, but then Id be storing all that in the stack, and everything really needs to go in the heap. The other way of doing everything, is have different lists of memory for each item. For example I have a turret vector that keeps all the active alive turrets in it. This would be seperate to the ship vector. The thing I don't like about that is it's a mixed list of turrets owned by different parents. When a parent dies, I have to clean up the list and removed the childen turrets. I am also treating the player as its own structure. Player.x Player.y Player.Thrust Player.Ship.Turret[x]->x; And now im thinking this is a bad idea because I have to calculate the ships in the vector and then calculate my player. For example lets say I have a function that manages all the turret code, which I do and you can see it if you download Black Sky. But in this function I go Player.Ship.Turret[x]->blah So after I go through the player turrets, I would have to do all new code to do the ships in the ship vector. I'm thinking perhaps a better solution would be is have the player CONTROL a ship inside the ship vector, instead of having his own structure that he controls. For example, the first in the vector, ships[0] could be the ship the player controls. All the other ships in the vector are AI controlled. I just wanted some feedback on how people would go about this, because as the game becomes more and more Robust, it also becomes more and more complex. Sorry for the long post, probably hard to understand, sorry for typos and being vague.
Black Sky A Star Control 2/Elite like game
Advertisement
I'm sorry I do not have a complete reply yet for you - but I am working on one! Just to let you know [smile]. I will get back to you soon on my opinions.

- Drew
Wow, that's a long post...:) (sorry but I didn't have the strength to read through it all).

Anyway about game loading. I would suggest an abstract factory for serialisable objects. I also suggest writing your own binary io-stream class (at least I have never got the ones in the std-lib to write anything else than ASCII).

class serialisable{public:  virtual ~serialisable() {}  virtual const char *type() = 0;  virtual void read(bin_istream &in) = 0;  virtual void write(bin_ostream &out) = 0;};class turret : public serialisable{  int shots_to_fire_;public:  virtual const char *type() {return "turret";}  virtual void read(bin_istream &in) {in >> shots_to_fire_;}  virtual void write(bin_ostrean &out) {out << shots_to_fire_;}};class quad_rail_gun_turret : public turret{  // add data herepublic:  virtual void read(bin_istream &in) {turret::read(in); /*read other data*/}  virtual const char *type() {return "quad_rail_gun_turret";}};

For the factory you can use http://www.cuj.com/documents/s=7994/cujcexp1906hyslop/
or roll your own.
I could post my own bin_*stream classes but I need to access another computer to fetch them. I you want I'll get back to you on that.
Instead of read and write, have a serialize function in each savable object that takes a SerializeStream pointer, and then do 'SerializeStream.doit(shots_to_fire_)'(doit is probably a bad name, I just didn't want to mix it up wih the serialize function each object will have). The trick is that you have two derived classes, SerializeWriteStream and SerializeReadStream, and the doit function takes a reference to the type(so when reading, the ReadStream can overwrite the variable). Then, you can either specialize the templated serialize function for basic types (int, float, etc) and make the generic form call the .serialize function of the object passed in OR use an actual C++ stream (string stream or file stream or whatever) that has already done that for you and just make the user(you in this case) provide the operator << and operator >> for any non-POD types they want to output (and those functions would probably just call the serialize functions for the type).
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
OkViperG – this I what I have to say (note this post is long as heck!).

Quote:
Ok I need some help. I'm currently making the game Black Sky.

I'm working on adding file loading, which will allow me to keep variables outside the game, and work on the game balancing without re-compiling.

But i'm running into coding problems when it comes to how I'm going to manage my data.


I think it is great you are using the data-driven model approach! You will definitely reap its benefits. I would like to ask what type of files are you loading – regular text files or some special formatted? I know that I used XML for my projects and it is really nice. I made my own little parser for it as well, but there is one, TinyXML available. I have not yet used it, but I heard good things about it.

Quote: Currently, I was going to/am doing the following:
Game loads all ship components (engines, sensors, reactors, weapon designs, etc)
Then it loads all the spaceships. Now, what it needs to do is have no limit in the design of how many components of each kind, a ship can have. Example.

Lets say I have a spaceship, and it has 4 engines, 2 reactors, 25 turrets, 3 sensors.

The 20 of the 25 turrets are twin firing blah blah. The other 5 are quad rail guns.

So after I set up this ship externally to the game, I have to load it into the game. The problem is that there can be no limit to how many ships, components can exist (within reason, im not hardcoding anything in)

This means my GameData template has to be able to do dynamic templates.

So when I create a ship on my screen, it looks up which ship I want to create, then copies the information it needs into my ship vector.

Now my question is what would be the simplist and easiest way to do this system.



In terms of storing this or setting it up here is one idea. You know that you are going to need dynamic structures for the classes since a lot can be changed/customized. For this reason alone I would say use the STL vector class. It is exactly what you need.

With that said here is an example of what you could do.

struct engine{… functions / data …};struct reactor{… functions / data …};struct turret{… functions / data …};struct sensor{… functions / data …};class ship{private:vector <engine> EngineList;vector <turret> TurretList;…};


That is one approach. Here are the pros and cons.

Pros – you know what vectors are used for what so you do not need to iterate just one list and find what you need.
Cons – very specific – no way to easily add more components if you wanted to.

Here is the other option that is the opposite of that:

struct Components{.. stuff all components will share …// maybe ID for the type, hp (if it is destroyable),etc};struct Hardware{.. stuff all components will share …// maybe ID for the type, hp (if it is destroyable),etc};struct Weapons{.. stuff all components will share …// maybe ID for the type, hp (if it is destroyable),etc};struct engine : public Hardware{… functions / data …};struct reactor : public Hardware{… functions / data …};struct plasma_turret : public Weapons{… functions / data …};struct laser_turret : public Weapons{… functions / data …};struct sensor : public Component{… functions / data …};class ship{private:vector < Component > components;vector < Weapons > weapons;vector < Hardware > hw;…};


As you can see with this design it well define some base classes that you use to “categorize” the different parts of the ship – in this case, Weapons (anything that does damage or affects another ship) , Hardware (ship stuff that is *required* for the ship to work, engine, reactor, armor, etc), Components (additional things to add, such as a sensor, radar, etc).

I think by doing this you can have a good separating yet keep everything maintainable. I choose to present this way because it is far superior to making just one base class that everything will be derived from. In that design, you would have to iterate through everything and search for what you want to update and use. With this design, you have more control lover what components group you want to work with.

Quote:
My other problem is how do i link everything together. For instance, I could have 10 different ships on the screen, each ship would have it;s own turrets, engines, internal system etc.

For example, I have to cycle through everyship everyframe and update it. This includes, cycling through all the turrets of each ship, if the ship has any, and update those.

Right now im using structures with vectors. The hard part it about it all, is each system is pretty unique. I can have a ship, that has 10 DIFFERENT turrets on it. Each turret has it's own firing rate, turn rate, and shoots a different weapon.

I'm using raw pointer structures inside of vectors.

Basicly the way I handle it is like this, I have a vector of entities, that store x location, y location, its current heading and so on. So really it's a vector of a structure. Pointer structures. Inside this entity structure, I have another structure, Called Ship. Ship contains all the information about the ship it's self (how fast it can go, its armor, all that stuff).Inside the Ship structure as well, I have an individual vector for each component. Turrets, engines, sensors, reactors, etc.



If you do the design approach that I have mentioned, what oyu could do is this: Let’s say we will be working with the Weapons list:

[source lang = “cpp"]struct Weapons{	virtual void update() = 0;virtual void fire() = 0;virtual void reload() = 0;	int ID;};struct GatlingTurret : public Weapons{void update(){.. code ..}void fire(){.. code .. // fire really fast!}void reload(){.. code .. // reloads slower!}};class ship{private:vector < Component > components;vector < Weapons > weapons;vector < Hardware > hw;…Public:Void Destroy(){// cleanup what ever here}Void update(){// iterate through each item of each vector and call the Update for( std::vector< Components >::itterator itr  = components.begin(); itr != components.end(); itr++)(*itr).Update(); // updates all components!// function of course you will need to add in the virtual void update = 0 to all base classes}void FireWeaponGroup (int weapon) // fires a weap group!{for( std::vector< Weapons>::itterator itr  = weapons.begin(); itr != weapons.end(); itr++){if ( (*itr).ID == weapon)(*itr).Fire();}}// just one example if you only wanted to fire single weapons, you could use the indexed approach with this method as well};


I hope you can see how good this system is. If you do not I will elaborate for you, but right now I am going to move on.

Quote:
So basicly if I wanted to access something, I would have to go like

shipent[index]->Ships->Turret[x]->heading=0;

For example lets say I have 10 ships, each ship has 2 turrets, each turret shoots 2 weapons in a cycle fire.

My first loop goes through the ships (movement code, ai)
the first nested loop goes through the turrets (it updates where its aiming at)
if the turret has more than 1 weapon, I have to loop # of weapons on the turret
I see if the weapon is ready to fire, then I spawn in a laser particle, based on where the turret is.

When the ship dies, I have to manually delete all the pointers (turrets, weapons on turrets).
Then deletee the entity its self. then erase the index the ship is in in the vector.


If you use my method – you will have a lot less to give you headaches with! What I suggest next is making a ShipManager class that maintains all ships. I will show you an example of a manager class that I have made for an engine component of mine, but you should be able to se how you can apply it for your game. I am in the process of making a templated version but this is some code:

[source lang ="cpp"]class Ship_Manager{private:	std::vector <Ship> surfaces;	std::vector <string> names;	int FindNextSlot();	void Remove(int ind);	int NameToInt(char* name);	ship* NameToShip(char* name);	ship* GetShip (int val);	ship* GetShip (char* name);public:		Ship_Manager ();	~ Ship_Manager ();	int Add(Ship &ship, char* RefName);	void Remove(char* filename);	void Cleanup();		Ship* operator()(char* name);};


This is the header. It will not match a good design for you because it was meant for SDL_Surface*’s, but I can make something more appropriate and send the example to you later.

Immortal_SDL_Surface_Manager::Immortal_SDL_Surface_Manager(){	surfaces.resize(10);	for(int x=0;x<10;x++)		surfaces.at(x).data = 0;	names.resize(10,"");}Immortal_SDL_Surface_Manager::~Immortal_SDL_Surface_Manager(){	Cleanup();}void Immortal_SDL_Surface_Manager::Remove(char* name){		int index = NameToInt(name);	Remove(index);}void Immortal_SDL_Surface_Manager::Remove(int index){	SDL_FreeSurface( surfaces.at(index).data );	surfaces.at( index ).data = 0;	names.at( index ) = "";}int Immortal_SDL_Surface_Manager::FindNextSlot(){	int index = 0;	for(index = 0;index<(int)surfaces.size();index++)	{		if(strcmp( names.at(index).c_str(),"") == 0 )			break;	}	return index;}int Immortal_SDL_Surface_Manager::Add(SDL_Surface* surface, char* RefName){	int index;	index = FindNextSlot();	if(index >= (int)surfaces.size())	{		surfaces.resize( (surfaces.size() + 5) );		for(int x = (int)surfaces.size() - 6;x<(int)surfaces.size();x++)			surfaces.at(x).data = 0;		names.resize( (names.size() + 5) );	}	surfaces.at(index).data = surface;	if(!surfaces.at(index).data)		return 0;	names.at(index) = RefName;	return 1;}void Immortal_SDL_Surface_Manager::Cleanup(){	for(int x=0;x<(int)surfaces.size();x++)		Remove(x);	surfaces.resize(0);	names.resize(0);}SDL_Surface* Immortal_SDL_Surface_Manager::GetSurface(int val){	return surfaces.at(val).data;	}SDL_Surface* Immortal_SDL_Surface_Manager::GetSurface(char* name){	for(int x=0;x<(int)names.size();x++)	{		if(names.at(x).size() > 0 && ( strcmp(names.at(x).c_str(),name) == 0 ) )			return surfaces.at(x).data;		}	return 0;}SDL_Surface* Immortal_SDL_Surface_Manager::NameToSurface(char* name){	int ind = 0;	for(std::vector<string>::iterator itr = names.begin(); itr != names.end(); itr++,ind++)	{		if( strcmp( (*itr).c_str(), name ) == 0 )			return surfaces.at(ind).data;	}	return 0;}int Immortal_SDL_Surface_Manager::NameToInt(char* name){	int ind = 0;	for(std::vector<string>::iterator itr = names.begin();itr != names.end(); itr++,ind++)	{		if( strcmp( (*itr).c_str(), name ) == 0)			return ind;	}	return -1;}SDL_Surface* Immortal_SDL_Surface_Manager::operator()(char* name){	return NameToSurface(name);}


I didn’t rename all the stuff, b/c I just wanted you to get an idea of how it could work. To use this system you could do this –
Ship_Manager good;Ship_Manager bad;good.addnewship(“good ship 1");good(“good ship 1")->SetAllData();bad.addnewship(“bad ship 1");bad (“bad ship 1")->SetAllData();...Update(){good.Update(); // need to add this to the manager class, but all it will do is loop though and update all the ships!bad.Update(); // update will also remove any ships that “die" as well as perform maintance on them.}


I hope you can at least see this design approach. I love it because it is really easy to maintain a lot of stuff with minimal code. The only draw back is a speed hit for using STL vector.

Quote:
Anyways you get the idea. Gamedata will store all the info by using vectors

At the moment I was trying to do something like

ship[x]->Ship=GameData.Ship[shipid]

This way I could just copy structure to structure.
In other words, it would copy the entire ship template, into the entity.Ship structure. When I create a ship, I can copy the entire "image" or template of the ship into the ship, meaning it becomes the template.

The biggest problem is GameData structure in order to keep everything in the heap I have to pass pointers into the vectors.
vector<struct ship*> Ship;

because if I did that, I would have to go
ship *Ship;

And that is giving me compile errors.
I have to go
struct ship *Ship;

which im not sure why. I think it has problem with doing pointer structures that have vectors in them.

Reason why I have to make ship into a pointer is because.
Ship= new ship;
Ship->shipstuff=x;
...
...
GameData.Push_back(Ship) this way I can have as many ships as I want (within reason again)

I could keep the ship struct not a pointer in the GameData structure, and just pass full structure through, but then Id be storing all that in the stack, and everything really needs to go in the heap.

The other way of doing everything, is have different lists of memory for each item. For example I have a turret vector that keeps all the active alive turrets in it. This would be seperate to the ship vector.
The thing I don't like about that is it's a mixed list of turrets owned by different parents. When a parent dies, I have to clean up the list and removed the childen turrets.



All of this will be taken care of during the design and implementation of the various functions in the manager as well as the classes themselves. You will not have to worry about maintaining pointers to this or that – all you will have to do is make one function call and everything will be done for you – I’d take that functionality over what you are having to do now any day [smile].

Quote:
am also treating the player as its own structure.

Player.x
Player.y
Player.Thrust
Player.Ship.Turret[x]->x;
And now im thinking this is a bad idea because I have to calculate the ships in the vector and then calculate my player.

For example lets say I have a function that manages all the turret code, which I do and you can see it if you download Black Sky.
But in this function I go Player.Ship.Turret[x]->blah
So after I go through the player turrets, I would have to do all new code to do the ships in the ship vector.

I'm thinking perhaps a better solution would be is have the player CONTROL a ship inside the ship vector, instead of having his own structure that he controls. For example, the first in the vector, ships[0] could be the ship the player controls. All the other ships in the vector are AI controlled.

I just wanted some feedback on how people would go about this, because as the game becomes more and more Robust, it also becomes more and more complex. Sorry for the long post, probably hard to understand, sorry for typos and being vague.


From all of this you should be able to see how you could handle the player as well. If not let me know and I can do some major example writing for ya.[wink]. I have to say I’m sorry for the even longer post [lol], but hopefully I have been of some help to you! Like I said, feel free to ask about anything I’ve said or how to do this or that, I’m available to help you out [smile]. Goodluck!

- Drew
Thanks for your feedback Drew. The problem is that I don't know how to use classes, and I'm not sure how long it would take me to understand them fully.

Right now im just using standard c, with some c++ things.
Last night I was able to get my system to work, but It seems sorta difficult.

I would have to say this is probably the hardest part so far in my project.
But then again, every single thing im doing is a crash course in programing, so I guess I can't complain.

I suppose in my free time I should take a look at how classes work. Other wise i'll just stick to my structure system for now.
I appreciate all the feedback, esp drew, thanks
Black Sky A Star Control 2/Elite like game
I strongly suggest you spend some time learning the basics of C++ (which means learning classes at least, and probably simple templates, and maybe polymorphism) before you waste tons of time hacking the same features through C. In this case, I think the effort would be well worth it.
Any good text on C++ should help you with such things. I can't really suggest any specific books as the ones I own aren't that great (I learned mostly from trial and error, google and gamedev).
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk

This topic is closed to new replies.

Advertisement