LoginSignup
13
10

More than 5 years have passed since last update.

早い・簡単・便利な Config ファイルクラスを作る (C++/OpenSiv3D)

Last updated at Posted at 2018-12-05

プログラムを再ビルドせずにパラメータを変更 できれば、ゲームやアプリを高速に開発できます。本記事では、TOML 形式のテキストファイルに int32double, String, Vec2, ColorF, Rect, Array<Point>, Array<String> といった型の値を記述し、プログラムの実行中に値を書き換えたときに、実行中のアプリケーションに反映されるような Config ファイルを扱う便利クラスを紹介します。
最短のケースで、テキストファイルに 1 行、プログラムに 2 行追加するだけで、実行中に更新可能な変数・配列を作れるようになります。

ライブラリ

Config.hpp をダウンロードしてプロジェクトに追加します。

使い方

設定記述ファイルをプロジェクトフォルダ (App/ フォルダ内) に配置します。
ここでは config.toml というファイルを作ります。
TOML の仕様については TOML仕様の和訳 が参考になります。

config.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 します。
これで、サンプルプログラムのように、いつでも値を更新できるようになります。

Main.cpp
# 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);
        }
    }
}

サンプルプログラム

少し規模が大きいサンプルです。
記述ファイルを変更すると、ウィンドウタイトルやサイズ、背景色、パラメータが変わります。

config.toml
[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"]
Main.cpp
# 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 クラスを導入して、高速な開発を実現しましょう。

13
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
10