Level data exporting and loading

Started by
7 comments, last by Headkaze 13 years, 3 months ago
I'm developing a C++ game for the iPhone which has recently started receiving low memory warnings from the kernel. I am very concerned about it and need to reduce the RAM usage as much as possible.

One area I can see this being improved is the way my C# level editor exports it's data. Currently it exports the data as a bunch of "C" structs.

Eg.

const GemDef g_DanjerReef_Gem_Actors[] ={	{ "", 64.53f, -46.25f, 0.00f, 5.00f, 5.00f, GEMTYPE_DIAMOND, "chip1" },};const ChainDef g_DanjerReef_Chain_Actors[] ={	{ "AnchorRope", 1.25f, 1.00f, 0.00f, 0.00f, "chain", 62.34f, 76.72f, 62.50f, 66.41f, 4, -0.50f, 1.00f, false, 0.00f, "Link", "Anchor", 2, 0, FILTERTYPE_ALL, FILTERTYPE_1 },	{ "", 1.88f, 1.00f, 0.00f, 0.00f, "seaweed2", 35.63f, -74.38f, 35.47f, -64.84f, 4, -0.60f, 1.15f, false, 0.00f, "v1", "", 1, 0, FILTERTYPE_ALL, FILTERTYPE_1 },	{ "", 1.88f, 1.00f, 0.00f, 0.00f, "seaweed1", 19.38f, -54.84f, 18.28f, -37.50f, 5, -0.60f, 1.13f, false, 0.00f, "V2", "", 1, 0, FILTERTYPE_ALL, FILTERTYPE_1 },	{ "", 1.88f, 1.00f, 0.00f, 0.00f, "seaweed3", 28.75f, 12.66f, 28.75f, 26.88f, 4, -0.60f, 1.13f, false, 0.00f, "V3", "", 1, 0, FILTERTYPE_ALL, FILTERTYPE_1 },	{ "", 1.88f, 1.00f, 0.00f, 0.00f, "seaweed4", -20.63f, 27.50f, -20.16f, 37.34f, 3, -0.60f, 1.14f, false, 0.00f, "V4", "", 1, 0, FILTERTYPE_ALL, FILTERTYPE_1 },	{ "", 1.88f, 1.00f, 0.00f, 0.00f, "seaweed4", 59.06f, -65.00f, 59.38f, -49.06f, 5, -0.60f, 1.14f, false, 0.00f, "V5", "", 1, 0, FILTERTYPE_ALL, FILTERTYPE_1 },};const LevelData g_DanjerReef_LevelData ={	LEVELSTATUS_FINISHED,		// Status	"Danjer Reef",		// Name	"danjerreef.tmx",		// MapFile	LEVEL_DANJERREEF,		// Level	LEVEL_TO_THE_CANOPY,		// NextLevel	LEVELTYPE_WORLDEND,		// LevelType	1024, 1024,		// Width, Height	WORLD_EASTERISLAND,		// World	0,		// EnterType	0,		// ExitType	"Deep_Deep_Wallow.m4a",		// Music	23, 26,		// MinZoom, MaxZoom	LEVELFLAGS_COLLECT_SPECIAL_MASK,		//Flags	-72.66f, 63.44f,		// StartX, StartY	g_DanjerReef_Platforms,		// PlatformData	86,		// PlatformCount	g_DanjerReef_Actors,		// ActorData	16,		// ActorCount	50,		// TotalActors	g_DanjerReef_Sounds,		// SoundData	17,		// SoundCount};


I have considered exporting a binary format but can see some headaches like the fact the data needs to be compiled for both i386 (simulator) and armv6 (hardware) so I can imagine endianness, float formats, internal data structures etc. all causing potential problems here.

Another solution I had was to implement the GCC compiler to compile the "C" code into binary for loading directly into the game. Unfortunately I have no idea how I would go about this.

Finally I'm guessing something like xml would be a format to consider. I believe it would be quite slow and a tonne of code to process the xml for all the different actors in the game.

So now to my questions. What would be the best solution here and how would I implement it?
Advertisement
The important thing isn't what your editor does on the development side, it's what your game engine does on the loading side. How do you load, parse, and then store the data from the excerpt you posted? What's the in-game representation of that data look like?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

To give you the idea of how the data is "loaded" I'll use the Actors' as an example. All Actor's are subclasses of the Actor class. Each one has it's own struct which is the basic data storage format for it's exported data from the level editor. Using the struct it's associated class is instanciated.

Here is a snippet of the loop and switch/case that instanciates the actors for a level

for(int i=0; i<g_levelData->ActorCount; i++){			switch(g_levelData->ActorData.Type)	{		case ACTORTYPE_PLATFORM:		{			break;		}		case ACTORTYPE_PLAYER:		{			break;		}		case ACTORTYPE_CRATE:		{			const CrateDef* crateDef = (const CrateDef*) g_levelData->ActorData.Data;						for(int j=0; j<g_levelData->ActorData.Count; j++)				g_actorArray.push_back(new Crate(crateDef[j].Name,												 Vector3DMake(crateDef[j].X, crateDef[j].Y, crateDef[j].Z),												 SizeFMake(crateDef[j].Width, crateDef[j].Height),												 crateDef[j].Skin,												 crateDef[j].DropEmit,												 crateDef[j].Friction));			break;		}		…	}}


In the above you can see if the level uses a "Crate" actor it will instanciate a Crate object and add it to the g_actorArray.

Here is part of the Crate class's header file

struct CrateDef{	string Name;	float X;	float Y;	float Z;	float Width;	float Height;	string Skin;	MaskType DropEmit;	float Friction;};class Crate : public Actor{public:	Crate(const string& name, Vector3D position, SizeF size, string skin, MaskType dropemit, float friction);	~Crate();		void Update(float elapsedTime);	void Render();	private:	MaskType DropEmit;	int CrateBounce;	ParticleEmitter* CrumbleEmitter;	Actor* MaskActor;};
Have you pin-pointed where exactly during the execution of your program you run into the low-memory situation?

The first thing that strikes me about your approach is that it is highly wasteful; you duplicate all the information about an actor by storing it both in the struct and in the final instance that is loaded into game state. Why aren't you just deserializing the actors directly from the data stream? At the very least this should (roughly) halve your memory requirements during load-time.


I also notice that you pass some data by value into your Crate constructor, where you should at the very least be passing by const reference. This will bloat your memory requirements as well, not to mention increasing load times by invoking a lot of copy operations needlessly.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Quote:Original post by ApochPiQ
Have you pin-pointed where exactly during the execution of your program you run into the low-memory situation?


The iPhone has notoriously bad memory management and it seems to change based on what has been previously running in memory etc. So it's hard to tell but I am currently profiling to find out.

Quote:Original post by ApochPiQ
The first thing that strikes me about your approach is that it is highly wasteful; you duplicate all the information about an actor by storing it both in the struct and in the final instance that is loaded into game state. Why aren't you just deserializing the actors directly from the data stream? At the very least this should (roughly) halve your memory requirements during load-time.


It does seem very wasteful I agree but it was the easiest way for me to export the data from a C# level editor running on an x86 PC to a C++ program compiling on ARM. I've measured the current total level data and it comes to 0.28 MB which does not seem to be an awful lot in the context of things considering I use about 5 MB for textures. I would have no idea how to deserialize data from a C# program into a C++ app. Any ideas on that?

Quote:Original post by ApochPiQ
I also notice that you pass some data by value into your Crate constructor, where you should at the very least be passing by const reference. This will bloat your memory requirements as well, not to mention increasing load times by invoking a lot of copy operations needlessly.


Yes I see what you mean the "skin" parameter should be passed in as a "const string&"
Before I get too far, let me clarify one thing: are you storing your "C struct format" stuff in a text file and reading that to load into the game, or are you actually saving the structs as code and compiling that into the game binary itself?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Quote:Original post by ApochPiQ
Before I get too far, let me clarify one thing: are you storing your "C struct format" stuff in a text file and reading that to load into the game, or are you actually saving the structs as code and compiling that into the game binary itself?


I'm saving it as code and compiling it into the game binary.
So... why not change your serializer to output calls to your class constructor directly? Eliminate the middle-man, so to speak [smile]

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Quote:Original post by ApochPiQ
So... why not change your serializer to output calls to your class constructor directly? Eliminate the middle-man, so to speak [smile]


This sounds like a good option but how would I actually structure the output to do it? Seems like I would need to export a large switch/case and then "new" the appropriate class. Is this the only way?

This topic is closed to new replies.

Advertisement