😽

C++ ゲーム開発者は Live++ を使おう

2024/12/07に公開

C++ ゲーム開発者は Live++ を使おう

この記事は、Siv3D Advent Calender 2024 8日目の記事です。
C++ / Siv3D ゲーム開発に Live++ を導入したら生産性にブレイクスルーが起こったので紹介します。

Live++ とは何なのか

  • C/C++ でホットリロードを可能にする商用の開発ツール。
  • 無料トライアルのあとは月 €11.90 (約2000円) が必要。ただし、学生は無料で利用可能。
  • IDE に依存せず利用できる。Visual Studio でも Rider でも使える。
  • Windows、X BOX SERIES X|S、PS5 の開発に対応している。

Live++ のホットリロードは何がすごいのか

Live++ を導入すると、C++ ソースコードを変更した際にゲームを動作させたままソースコードの変更を適応することが出来ます。例えば、以下のような場面で重宝します。

  • 例1. ゲーム実行中バグが発生したので、ゲームを動かしたままオブジェクトの座標を Print 表示したい。
void UpdatePlayer() {
+    Print << U"Pos: " << m_position; // Reload ➡️ OK
  ...
}
  • 例2. ゲームを動かしたまま、UI の形状や色を変えてスタイルを調整したい。
void DrawUI() {
-    RectF(Scene::Center(), SizeF{20, 20}).draw(ColorF{1.0, 0.9, 0.8});
+    Circle(Scene::Center(), 20).draw(ColorF{0.2, 0.3, 0.4}); // Reload ➡️ OK
}
  • 例3. ゲームを動かしたまま、敵の行動ルーチンのアルゴリズムを試行錯誤しながら実装したい。
void UpdateAI(AI& ai) {
-    ai.executeAlgorithm_Battle();
+    if (ai.hasFood()) ai.executeAlgorithm_Eat();  // Reload ➡️ OK
+    else ai.executeAlgorithm_Search(); // Reload ➡️ OK
}

このいずれの場面でも、ソースコードを変更した際は直ちに実行中のゲームそのものに反映出来ます。ホットリロードにかかる時間はコードの内容によりますが、中程度 (500 行未満) のソースファイルの場合 0.5 ~ 1.0 秒ほどで終わる印象です。軽めのファイルなら更に爆速で終わります。

スクリプト言語のホットリロードの代替として

従来、このようなホットリロードを実現するために、現場では組み込みスクリプト言語 (Lua など) を用いると効果的とされきました。しかし、組み込みスクリプト言語の運用は複雑な API バインドや融通の効かないデバッグ、パフォーマンス的課題に難があるという点でデメリットもあります。

一方、Live++ を用いればスクリプト言語を用いずとも C++ のホットリロードが実現出来るため、開発者は一つの言語で構成されたプロジェクトに専念出来るようになり開発効率の向上が期待できます。

Visual Studio ビルドインのホットリロードと比較して

Visual Studio にはデフォルトで C++ ホットリロード機能がついているので、使ったことがある方もいらっしゃると思います。しかし、Visual Studio のホットリロードには対応できない変更がある程度存在することが知られています。例えば、外のオブジェクトを参照するコードを追加したときや、変更するコード量が一定以上多くなったときに頻繁にホットリロードが失敗します。「この変更はホットリロードに対応していません」と失敗したメッセージが高頻度で表示されるので気が滅入ってきます。

反面 Live++ のホットリロードは、押し並べてほぼ全てのコード変更の適応が成功します。Live++ はコンパイルさえ通ればホットリロードが可能であり、非常に優秀です。ただし、クラスのメモリレイアウトを変更する場合などはプログラマ側で対処が必要です (後述)

導入方法

Live++ は簡単にプロジェクトへ導入できます。

  1. まず、Live++ をダウンロードします。以下のページから zip をダウンロードします。内容はAPIのヘッダとDLL、実行ファイル合わせて8ファイル程度あります。

    https://liveplusplus.tech/trial_download.html

  2. スタートガイドに沿って進めます。

    https://liveplusplus.tech/docs/documentation.html#compiler_settings

  • 先ほどダウンロードした zip の中身をプロジェクト直下に配置します。
  • ドキュメントに記載されていたコンパイルオプションとリンカーオプションをそれぞれ設定します。
  1. プロジェクト内で初期化時に Live++ を起動するプログラムを書きます。プログラム実行時、Main が呼ばれる前に Live++ のクライアントが起動するようにします。以下に、Siv3D 用の Live++ アドオンの例を示します。Main() 関数の最初などで InitLivePPAddon() を呼んでください。相対パスは各自のプロジェクトに合わせて変更してください。

使い方

アプリケーションの実行中、ソースコードを変更すれば Ctrl + Alt + F11 を押すだけで自動でホットリロードされます。ホットリロードに成功すると、トースト通知が表示されます。

注意点など

注意 1: 関数を抜けてから適応される

関数の実行途中でホットリロードを走らせた場合、その変更は適応されません。

例えば、以下のプログラムではホットリロード自体は成功しますが、コード変更が適応がされません。

void Main()
{
	InitLivePPAddon();

	while (System::Update())
	{
-		if (KeyA.down())
+		if (KeyB.down()) // Reload ➡️ 適応されない
		{
			Print << U"Key Down!";
		}
	}
}

メインループに対してホットリロードしたいときは、該当箇所を関数 (クラスのメソッドも可) にしておき、ホットリロードするときにはその関数から抜けている必要があります。

void MainProcess() 
{
-		if (KeyA.down())
+		if (KeyB.down()) // Reload ➡️ OK
		{
			Print << U"Key Down!";
		}
}

void Main()
{
	InitLivePPAddon();

	while (System::Update())
	{
		MainProcess();
	}
}

これは、コルーチンでも同様です。すでに走っているコルーチンある状態でホットリロードすると、そのコルーチン自体の関数処理には適応されませんが、新しく実行されたコルーチンではホットリロードが適応されます。

注意 2: メモリレイアウトの変更について

上述の通り、クラスメンバの追加などの操作により、メモリレイアウトが変わってしまうとエラーになってしまいます。ホットリロード自体は成功しますが、既存の古いメモリレイアウトのデータを新しいメモリレイアウトとして取り扱い、アクセス違反を行う挙動になってしまうためです。

ただし、プログラマ側でこの対処を行うことで、メモリレイアウト変更のホットリロードも一応可能なようです。若干手間のかかる印象を受けたので、私はホットリロードをかけるときにそもそもメモリレイアウトの変更を行わないことにしています。

https://liveplusplus.tech/docs/documentation.html#API_structural_change

非静的変数以外、つまりグローバル変数や静的変数の追加は問題なくホットリロード可能です。関数やメソッドの追加なども問題ありません。

class Dog {
  Vec2 pos;
+  Vec2 vel; // Reload ➡️ Warn! メモリレイアウトの変更
}

Dog s_dog1;
+  Dog s_dog2; // Reload ➡️ OK

void fn () {
+  Dog d1; // Reload ➡️ OK
+  static Dog d2; // Reload ➡️ OK
...
}

+  void fn(Dog dog) { ... } // Reload ➡️ OK

おわりに

私はまだ数ヶ月前に Live++ を使い始めてばかりですが、開発効率を爆速にする革命的な開発ツールだと実感しています。学生なら無料の Educational license をメールで申請出来るのでぜひ導入してみてはいかがでしょうか。

宣伝 ⬇️ Siv3D で作っているゲームを Steam で来年リリースします。ウィッシュリストに追加していただけたら幸いです!世界最速メトロイドヴァニアです!!

Discussion