Debug vs Release (Memory Leaks)

The blank solution (which can be downloaded), has two configurations, Debug and Release. In Debug mode, you are able to trace through the code step by step. The Release version is the final version and it produces much faster code.

Using C++, you as the programmer are responsible for allocating and releasing memory resources. Not taking care about memory allocations and messing up the heap (memory used for dynamic allocations) results in bad design and unstable code. To have a strategy avoiding memory leaks, helps writing solid code. To see what i mean, I first explain the term memory leak and then i show you how to avoid problems like these.

Example:
void func1()
{
	int* ptrInt = new int;
	*ptrInt = 10;
	std::cout << "value of int value = " << *ptrInt << std::endl;
}

void main()
{
	func1();
}

Function "func1( )" allocates data on the heap (32 bit), writes data into memory and prints it on the screen. After that the function returns and the pointer to the allocated memory adress gets lost. The memory block still exists. The left allocated data block (4 byte) is called a memory leak.
The engine helps you finding memory leaks very fast. All you have to do is add two lines of code, so that it looks like this.

Example:
void func1()
{
	int* ptrInt = SCE_NEW int;		// added
	*ptrInt = 10;
	std::cout << "value of int value = " << *ptrInt << std::endl;
}

void main()
{
	SCE_REPORT_MEM_LEAKS			// added
	func1();
}

These two lines of code tell you where to find the memory leak in the code. (only in Debug!)
After closing the application (game), the logging system of the engine will write something like this:

Example:
Detected memory leaks!
Dumping objects ->
c:\source\main.cpp(88) : {131} normal block at 0x010E6678, 4 bytes long.
 Data: <    > 0A 00 00 00
Object dump complete.

This heap dump gives you information about each memory block that has been allocated and not freed. It tells you in which file the data is allocated, the line number and size of the memory block!





Logging (Tracing) and Asserting

Logging (Tracing) helps you keeping book about what is happening. Asserts help you to check for errors in calculations, or to make sure the right parameters are passed to a function. The classical assert example is to check, if a division by zero is going to happen.
Both mechanisms write the desired info into a file, stdout, the debug window of the compiler and the game internal console.
Asserting is only enabled in Debug mode, Tracing works in both modes: Debug and Release.

To see these mechansims in action have a look at the following example:
Example:
void func1()
{
	// logging (=tracing)
	SCE_TRACE("This is the start of function: func1\n");

	int a = 1;
	int b = 2;
	int c = a+b;

         // check if calculated value is the desired one
	SCE_ASSERT_1(c == 3);
	SCE_ASSERT_2(c == 3, "Check variable c, it is not equal 3\n");

	// inform user
	SCE_TRACE("End of function: func1, and the calculated value = " << c << "\n");
}




Using the Database

Every game has to handle a lot of data. The data can be geometry data, textures, audio files, scripts, ... Materials for example can take a couple of textures (bitmaps) and combine them, to define a new effect. Scripts can reference data files, set a couple of paramters and define for example a new particle effect. To make things and life easy, all the assets are stored in a predefined directory structure, that can be accessed and extended.

Using the blank solution from the download section, you will recognize that there are two major directories. One for the "source" code and one that is called "exec_game". The "exec_game" directory is the one of interest for now.

After compiling and linking in Debug (or Release) mode, you will find the following items in the "exec_game" directory (relative to this directory, all asset files are placed!) :

In the data directory the following six directories can be found:

Looking into these directories, you will find image, sound and graphics files. There are also all kind of different script files. These files can be opened with a normal ascii editor like notepad. You should take a few minutes and study these directories to get an overview.





Scripting

The engine makes heavy use of scripting. Talking about scripts does not mean programming game or engine behaviour using a high level programming language like basic or java. Scripting here means: defining structures with parameters for all kind of objects like entities, materials, particles, ... . So the scritping is more like an advanced use of configuration files.

The engine has enough functionality to do comfortable scripting. I give you a couple of examples to show you how to use the classes and functions. (The concept is similar to that using XML, instead of using "< >" you use "{ }".)

Using configuration files and defining parameters in ascii files is common practice and it shortens development time. Instead of hardcoding (writing the configuration values into your source code and recompiling them everytime they change), use the engine scripting mechanism. Believe me it is worth the time spending a view minutes learning how to use it.

Script Example for an Entity
Taken from script file: "mech.cfg" located in the entity directory for entity mech.
-----------------------------------------------------------------------------------

castShadows	1
pickable	1

graphicLodLevel_0
{
	model	mech.graphModel
	view	min 0 max 10
}

The script file holds two lines setting the flags: "castShadows" and "pickable". After that two parameters are collected in a block called: "graphicLodLevel_0". This block holds information about graphics data and viewing that graphics data. (For now there is no need to understand the params, the example should just show you the mechanisms of scripting.)

In the following i show you how to use the existing scripting functionality to read the values and how to handle blocks:

Example: C/C++ code reading the above script
// first there have to be vars and structs that hold the values
// i collect everything in a structure and call it Entity

struct Entity
{
	bool castShadowFlag = false;
	bool pickableFlag = false;

	struct GraphicLodModel
	{
	         std::string     file_;
	         float   minDistance_;
	         float   maxDistance_;
	};

	GraphicLodModel grModel_;
};

// loading code
void loadGraphicLodModel(Script& script, GraphicsLodModel& grModel)
{
	while( script.getCurrentLineIndex() != script.numOfLines() )
	{
		LineInfo& info = script_.readLineInfo(script_.getCurrentLineIndex());

		if( info.getNumTokens() == 2 && info.getToken(0) == "model" )
	        {
			grModel.file_ = info.getToken(1);
		}
		else if( info.getNumTokens() == 5 && info.getToken(0) == "view" )
		{
			grModel.minDistance_ = atof(info.getToken(2).c_str());
			grModel.maxDistance_ = atof(info.getToken(4).c_str());
		}
		else if( info.getNumTokens() == 1 && info.getToken(0) == "}" )		// block is finished so return
		{
			return;
		}

       		script.getCurrentLineIndex()++;
	}
}

void loadEntity(Entity* ent, const std::string& scriptFile)
{
	Script script;
	script.open(scriptFile);

	while( script.getCurrentLineIndex() != script.numOfLines() )
	{
		std::string token = script.readFirstLineToken(script.getCurrentLineIndex());

		if( token.find("graphicLodLevel_") != std::string::npos )
	        {
	        	script.getCurrentLineIndex()++;
			loadGraphicLodModel(script, ent->grModel_);
		}
	        else if( token == "castShadows" )
	        {
		        script.readLine(script.getCurrentLineIndex(), token, ent->castShadowFlag);
	        }
	        else if( token == "pickable" )
	        {
	        	script.readLine(script.getCurrentLineIndex(), token, ent->pickableFlag);
	        }

       		script.getCurrentLineIndex()++;
	}
}
Example: C/C++ code writing the above script
// assuming the struct Entity is defined as above

void saveEntity(Entity* ent, const std::string& fileName)
{
	Script script;
	script.create(fileName);

	// write flags
	SCE_WRITE_LINE_TO_SCRIPT(script, "");
	SCE_WRITE_LINE_TO_SCRIPT(script, "castShadows\t" << ent->castShadowFlag);
	SCE_WRITE_LINE_TO_SCRIPT(script, "pickable\t" << ent->pickableFlag);

         // write graphics data
	SCE_WRITE_LINE_TO_SCRIPT(script, "graphicLodLevel_" << 0);
	SCE_WRITE_LINE_TO_SCRIPT(script, "{");
	SCE_WRITE_LINE_TO_SCRIPT(script, "\tmodel\t" << ent->grModel_->file << ".graphModel");
	SCE_WRITE_LINE_TO_SCRIPT(script, "\tview\tmin " << ent->grModel_.minDistance_ << " max " << ent->grModel_.maxDistance_);
	SCE_WRITE_LINE_TO_SCRIPT(script, "}");
}

This looks like a lot of code, but you will see this is no rocket science and it is worth the time using it.






SourceForge.net Logo