XML can be a nice solution, but if you just need regular settings like screen.width = 100, then you want a good old-fashioned .ini file that is human readable and has nice ASCII art
Just look at unreal, at least back when i tinkered with it. If you want a hierarchy, then the . character is that hierarchy:
##################
# Rendering #
##################
renderer.width = 100
renderer.height = 200 # some comment
renderer.fullscreen = true
Then create a static config:: system that either just treats the variables as plain text, which is just fine, or relates to specific variables, depending on your needs
XML is fine too, if you have a configuration tool for your game/application. I admit it's very easy today to make that kind of tool in .NET, and it might be a huge timesaver, depending on how familiar you are with the .NET framework.
If you want a solution in C++, here is one of mine:
Config.hpp
#ifndef __CONFIG_HPP
#define __CONFIG_HPP
#include <map>
#include <utility>
#include <string>
namespace platformer
{
class Config
{
static std::map<std::string, std::string> kv;
public:
static bool get(const std::string, const bool);
static int get(const std::string, const int);
static float get(const std::string, const float);
static std::string get(const std::string, const std::string);
//Changing the value in the key map.
template <typename T>
static void set( std::string v, T b )
{
if ( Config::kv.find(v) == Config::kv.end())
return;
kv[v] = b;
}
static bool load( std::string file );
};
}
#endif
Config.cpp
#include "config.hpp"
#include <fstream>
#include <iostream>
#include <sstream>
namespace platformer
{
std::map<std::string, std::string> Config::kv;
//Gets a bool value from map
bool Config::get(const std::string v, const bool def)
{
if ( Config::kv.find(v) == Config::kv.end())
return def;
bool b;
std::istringstream convert( kv[v] );
if ( !(convert >> b) )
return def;
return b;
}
//Gets an int value from map
int Config::get(const std::string v, const int def)
{
if ( Config::kv.find(v) == Config::kv.end())
return def;
int i;
std::istringstream convert( kv[v] );
if ( !(convert >> i) )
return def;
return i;
}
//Gets a float value from map
float Config::get(const std::string v, const float def)
{
if ( Config::kv.find(v) == Config::kv.end())
return def;
float f;
std::istringstream convert( kv[v] );
if ( !(convert >> f) )
return def;
return f;
}
//Gets a string value from map
std::string Config::get(const std::string v, const std::string def)
{
if ( Config::kv.find(v) == Config::kv.end())
return def;
return kv[v];
}
//Loading the config file into the keymap.
bool Config::load( std::string f )
{
std::fstream filestream;
filestream.open( f.c_str(), std::fstream::in );
if (!filestream) return false; // could not open file
//Reads one line in config file at a time and place it in the map.
while( !filestream.eof() )
{
char line[256];
filestream.getline( line, 256 );
std::string test( line );
std::string en;
std::string to;
bool temp = true;
//en is the value name and to is the value.
for ( std::string::iterator it=test.begin(); it!=test.end(); ++it)
{
if( *it == '#' )
break;
if( *it == '=' )
temp = false;
if( *it != '=' && *it != ' ' )
{
if(temp)
en += *it;
else
to += *it;
}
}
if( en.length() > 0)
kv.insert( std::pair<std::string,std::string>(en,to) );
}
filestream.close();
return true;
}
}
It's not very efficient, but it doesn't have to be!