プログラムを再ビルドせずにパラメータを変更 できれば、ゲームやアプリを高速に開発できます。本記事では、TOML 形式のテキストファイルに int32
や double
, String
, Vec2
, ColorF
, Rect
, Array<Point>
, Array<String>
といった型の値を記述し、プログラムの実行中に値を書き換えたときに、実行中のアプリケーションに反映されるような Config ファイルを扱う便利クラスを紹介します。
最短のケースで、テキストファイルに 1 行、プログラムに 2 行追加するだけで、実行中に更新可能な変数・配列を作れるようになります。
ライブラリ
◆ Config.hpp をダウンロードしてプロジェクトに追加します。
使い方
設定記述ファイルをプロジェクトフォルダ (App/
フォルダ内) に配置します。
ここでは config.toml
というファイルを作ります。
TOML の仕様については TOML仕様の和訳 が参考になります。
[Setting]
a = 123
b = "text"
c = "(0.1, 0.2, 0.3)"
d = [10, 20, 30, 40, 50]
記述されたデータ全てを管理する ConfigData
クラスを作ります。
struct Setting setting
のように、セクションごとに内部で構造体を定義すると扱いやすくなります。
void ConfigData::reload(const TOMLReader& toml)
では、それぞれの内部の構造体に対して S3DCFG_RELOAD_SECTION
をします。
それぞれの構造体は void reload(const TOMLReader& toml)
メンバを持ち、最初にセクション名で S3DCFG_LOAD_SECTION
したあと、すべての値に S3DCFG_LOAD_VALUE
または S3DCFG_LOAD_ARRAY
します。
これで、サンプルプログラムのように、いつでも値を更新できるようになります。
# include <Siv3D.hpp> // OpenSiv3D v0.3.1
# include "ConfigFile.hpp"
struct ConfigData
{
void reload(const TOMLReader& toml)
{
// 新しい構造体を作ったらここに追加
S3DCFG_RELOAD_SECTION(setting);
}
// [] セクションごとに構造体を作ると便利
struct Setting
{
int32 a;
String b;
ColorF c;
Array<int32> d;
// それぞれの構造体に reload 関数を定義する
void reload(const TOMLReader& toml)
{
// 先頭に S3DCFG_LOAD_SECTION
S3DCFG_LOAD_SECTION(U"Setting");
// LOAD_VALUE で値をロード
S3DCFG_LOAD_VALUE(a);
S3DCFG_LOAD_VALUE(b);
S3DCFG_LOAD_VALUE(c);
// 配列の場合は LOAD_ARRAY
S3DCFG_LOAD_ARRAY(d);
}
} setting;
};
using Config = ConfigFile<ConfigData>;
void Update(const Config& config)
{
Graphics::SetBackground(config.setting.c);
Print << config.setting.a;
Print << config.setting.b;
Print << config.setting.c;
Print << config.setting.d;
}
void Main()
{
// 設定記述ファイルのファイルパス
const FilePath configPath = U"config.toml";
Config config;
try
{
// Config ファイルを読み込み
config.reload(configPath);
}
catch (const ConfigError& err)
{
// Config の読み込みエラーがあると ConfigError 例外
err.show();
return;
}
Update(config);
while (System::Update())
{
// Config ファイルが更新されたら
if (config.hasChanged())
{
try
{
ClearPrint();
// Config ファイルを再読み込み
config.reload(configPath);
}
catch (const ConfigError& err)
{
err.print();
}
Update(config);
}
}
}
サンプルプログラム
少し規模が大きいサンプルです。
記述ファイルを変更すると、ウィンドウタイトルやサイズ、背景色、パラメータが変わります。
[Window]
title = "My Game"
size = "(1280, 720)"
[UI]
background = "(0.8, 0.9, 1.0)"
[Parameter]
a = 123
b = 3.1415
c = "text"
d = "(0.1, 0.2, 0.3)"
e = [10, 20, 30, 40, 50]
f = ["AAA", "BBB", "CCC"]
# include <Siv3D.hpp> // OpenSiv3D v0.3.1
# include "ConfigFile.hpp"
struct ConfigData
{
void reload(const TOMLReader& toml)
{
S3DCFG_RELOAD_SECTION(window);
S3DCFG_RELOAD_SECTION(ui);
S3DCFG_RELOAD_SECTION(parameters);
}
struct Window
{
String title;
Size size = s3d::Window::DefaultClientSize;
void reload(const TOMLReader& toml)
{
S3DCFG_LOAD_SECTION(U"Window");
S3DCFG_LOAD_VALUE(title);
S3DCFG_LOAD_VALUE(size);
}
} window;
struct UI
{
ColorF background = Palette::DefaultBackground;
void reload(const TOMLReader& toml)
{
S3DCFG_LOAD_SECTION(U"UI");
S3DCFG_LOAD_VALUE(background);
}
} ui;
struct Parameter
{
int32 a;
double b;
String c;
Vec3 d;
Array<int32> e;
Array<String> f;
void reload(const TOMLReader& toml)
{
S3DCFG_LOAD_SECTION(U"Parameter");
S3DCFG_LOAD_VALUE(a);
S3DCFG_LOAD_VALUE(b);
S3DCFG_LOAD_VALUE(c);
S3DCFG_LOAD_VALUE(d);
S3DCFG_LOAD_ARRAY(e);
S3DCFG_LOAD_ARRAY(f);
}
} parameters;
};
using Config = ConfigFile<ConfigData>;
void Update(const Config& config)
{
Window::SetTitle(config.window.title);
Window::Resize(config.window.size);
Graphics::SetBackground(config.ui.background);
Print << config.parameters.a;
Print << config.parameters.b;
Print << config.parameters.c;
Print << config.parameters.d;
Print << config.parameters.e;
Print << config.parameters.f;
}
void Main()
{
const FilePath configPath = U"config.toml";
Config config;
try
{
config.reload(configPath);
}
catch (const ConfigError& err)
{
err.show();
return;
}
Update(config);
while (System::Update())
{
if (config.hasChanged())
{
try
{
ClearPrint();
config.reload(configPath);
}
catch (const ConfigError& err)
{
err.print();
}
Update(config);
}
}
}
現時点での制約
「配列の配列」や「テーブルの配列」などを 1 行でロードできるマクロは未実装なので、各構造体の reload 内で、TOMLReader
の API を使って自前でロードを実装する必要があります。
まとめ
C++/OpenSiv3D プログラミングに Config クラスを導入して、高速な開発を実現しましょう。