OpenSiv3D | 開発効率を上げるサンプル集

公開:2020/10/14
更新:2020/10/14
3 min読了の目安(約2800字TECH技術記事

1. テキストファイルベースでの実行中の変数編集

値の微調整のためにビルドを繰り返す必要が無くなる機能です。
指定した数値、文字列、図形型の変数の値の一覧を自動的にテキストファイル QUICK_CONFIG.ini に INI 形式で書き出し、実行中にそのテキストファイル上で値が変更されたときにプログラムの変数の値を更新します。プログラムの中断や再起動はありません。

  • QC_BEGIN(); 最初に 1 度だけ呼び出します。
  • QC(value); 編集したい変数を指定します。
  • QC_END(); 変数のリストを INI 形式で書き出します。
  • QC_UPDATE(); テキストファイルの変更をプログラムの変数に反映させます。
# include <Siv3D.hpp> // OpenSiv3D v0.4.3

namespace s3d::Productivity
{
	class QuickConfig
	{
	private:

		using Variant = std::variant<int8*, int16*, int32*, int64*, uint8*, uint16*, uint32*, uint64*,
			float*, double*, bool*, String*, Point*, Float2*, Vec2*, Float3*, Float4*, Vec4*,
			Color*, ColorF*, HSV*, Circular*, Rect*, Circle*, Line*, Triangle*, Quad*, Ellipse*, RoundRect*>;

		std::map<std::string, std::pair<Variant, size_t>> m_data;

		FilePath m_iniPath = FileSystem::FullPath(U"QUICK_CONFIG.ini");

		DirectoryWatcher m_watcher;

	public:

		template <class Type>
		void capture(Type& value, const std::string& name)
		{
			m_data.emplace(name, std::pair<Variant, size_t>{ std::addressof(value), m_data.size() });
		}

		void generateINI()
		{
			Array<std::pair<std::string, Variant>> values(m_data.size());
			for (const auto& data : m_data)
			{
				values[data.second.second] = { data.first, data.second.first };
			}

			{
				TextWriter writer(m_iniPath);

				for (const auto& value : values)
				{
					std::visit([&](const auto p) {
						writer.writeln(U"{} = {}"_fmt(Unicode::Widen(value.first), *p)); }, value.second);
				}
			}

			m_watcher = DirectoryWatcher(FileSystem::ParentPath(m_iniPath));
		}

		void update()
		{
			for (const auto fileChanges : m_watcher.retrieveChanges())
			{
				if ((fileChanges.first == m_iniPath)
					&& ((fileChanges.second == FileAction::Modified) || (fileChanges.second == FileAction::Added)))
				{
					return read();
				}
			}
		}

		void read()
		{
			const INIData ini(m_iniPath);

			for (auto& data : m_data)
			{
				const String s = ini[Unicode::Widen(data.first)];
				std::visit([&](auto p) {
					*p = Parse<std::remove_pointer_t<decltype(p)>>(s); }, data.second.first);
			}
		}
	};

# define QC_BEGIN() Productivity::QuickConfig _quickConfig;
# define QC(value) _quickConfig.capture(value, #value);
# define QC_END() _quickConfig.generateINI();
# define QC_UPDATE() _quickConfig.update();
}

void Main()
{
	const Font font(30);

	QC_BEGIN();
	int32 a = 100;				QC(a);
	double b = 3.1415;			QC(b);
	ColorF bg = ColorF(0.1, 0.5, 0.3); 	QC(bg);
	String text = U"OpenSiv3D";		QC(text);
	Point pos(20, 30);			QC(pos);
	ColorF col(0.7);			QC(col);
	Rect rect(20, 90, 80, 20);		QC(rect);
	RoundRect rrect(200, 100, 100, 40, 8);	QC(rrect);
	QC_END();

	while (System::Update())
	{
		QC_UPDATE();
		Scene::SetBackground(bg);
		font(U"{}, {}, {}"_fmt(a, b, text)).draw(pos, col);
		rect.draw();
		rrect.draw();
	}
}