Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

プログラムを再ビルドせずにパラメータを変更 できれば、ゲームやアプリを高速に開発できます。本記事では、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 クラスを導入して、高速な開発を実現しましょう。

Reputeless
小さくなってもコードはモダン!▼ 未定義動作なしの名コーダー!▼ 実装はいつも 3 つ!!(GCC/Clang/MSVC)▼ OpenSiv3D, cppmap 作者
https://ryo-suzuki-contact.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした