『Lavender Quartz 境界秤動』ができるまで - Godot Engine の採用
『Lavender Quartz 境界秤動』 にエンジニアとして参加しました, 南と申します.
前の記事 に続き, 本記事では, 自分が担当した機能実装の流れを、時系列に沿って紹介していきます.
2024年12月

年末休みで暇だったので, 初めは本作のモックをぱっと作る気持ちでこの作品に参加しています.
当時は Godot Engine の C# 版を少し触っていた程度でしたが
- 使いごこちが良かったこと
- 要件が “ビジュアルノベル” と明確であったこと
- 国内での採用事例は多くないものの 2D タイトルでの実績が十分であったこと
- C# で書いているとどうしても長いコードになってしまう
これらの理由から, GDScript 版での開発を開始しました.
自作スクリプトの実装
最初は本作専用のスクリプト言語を設計・実装するところから着手しています.
- Ren’Py になるべく似ており, よりシンプルな文法
- Godot Engine を立ち上げることなく
.txtファイルの編集のみでOK
花式が Ren’Py の文法に慣れていたのもあり, スクリプトを自身で簡単に編集できるよう相談しつつ機能を決めていきました.
代わりに見えるようになったのは、残り火に照らされた灰のようにきらめく道路。それは地平線の向こうまで麦畑を割りながら、遠く遠く続いている。私の運転する車の両脇を流れていく。
私の村、私の家の畑。私を育てたこの景色。地の文は特別な文法を呼び出す必要がなく自然に記述できるようにしています.
改行を挟むことでページを分けられるようにしました.
set: iconu body: base hair: 1 face: confused arm: giveup
iconu:「なるほど」キャラクター表示と発話はこのような文法としました.


ゲーム内に表示されるキャラクターはすべて複数のレイヤーで表現されており
res://textures/characters/iconu/body_base.pngres://textures/characters/iconu/hair_1.pngres://textures/characters/iconu/face_confused.pngres://textures/characters/iconu/arm_giveup.png
が解決され合成されます.
シナリオファイルと同様に, 画像素材についても Godot Editor 内での操作が必要がないよう, 特定のディレクトリに <character>/<layer>_<name>.png という規則で書き出しを行い, これをスクリプトから解決するような設計としています.
最終的にはこのような文法となりました.
キャラクターのエイリアス機能なども実装したのですが結局あまり使われていなかったり, 振り返ると色々改善点はあるのですが, 汎用性を排除した専用スクリプトとしたことでシンプルな文法に着地できたように思えます.
2025年1月
Novel 画面の UI

年明けにはスクリプトの基本文法が固まっていたので, LM7 のコンセプトアートをもとに UI の実装を開始しました.

画面が縦に分割されている絵を Mock として実装し, 皆で触っています.
分割レイアウトはインパクトがありかなりよさげでしたが,テキストが下部に集中している文字の圧迫感や, 視線移動のつらさという問題がありました.

地の文は地なのでそのまま, 発言は漫画のフキダシのように発話者の近くをころころ移動するように変更しました. この段階で UI の方向性が概ね固まっています.
CI/CD
この時点で GitHub Actions を設定して, Web ビルドを回しながら全員で動作を確認しつつ進めています.
Godot の Export には abarichello/godot-ci を利用しています.
当初は GitHub の標準 Runner を使用していましたが, テクスチャ素材が増加したため(20GB), 最終的に Self-hosted runners と Cloudflare に自前の LFS サーバーを建てて運用しています.
開発中期からは game-ci/steam-deploy を利用して, Steam の Beta Branch での動作確認へと切り替えを行いました. リリース後の現時点でもこのフローでデプロイを行っています.
開発用ツールの追加

スクリプトを編集するにあたって以下のような機能が必要になってきました
- シナリオファイルをホットリロードできる
- 任意のシナリオファイル, ページにぱっとジャンプできる
- シナリオファイルのエラー表示とフォーマット
エディタ版のみ表示される開発用のツールとしてこれらの機能を順次実装しています.

大量のキャラクターの素材が生まれてきており, 更にレイヤーを組み合わせる方式により, 顔の把握が難しくなってきたので, 専用のビュアーを追加しました.
![]()
しかしメイン二人の表情差分は膨大で把握しきれず, 最終的にこのような画像が発生してスクリプトを書いていました.
本当に必要だったもの: 画像一枚とめちゃくちゃ短い type
セーブ/ローカライズ対応
セーブデータの設計を行いました, 以下の2点を要件としています.
- 最後に読んだページをそのままロードできること
- 表示言語を切り替えても、同じページをロードできること
この段階でローカライズの予定はあったので, それに耐えられる設計とする必要がありました.
locale/ ja_JP/ 00_00.txt zh_CN/ 00_00.txtlabel: c4b67848-1278-4710-8a50-87347468b4df私の村、私の家の畑。私を育てたこの景色。
label: cfeb7312-2065-4a78-9b7a-b6c8ff181d18それは昔からあまりにも当然のようにそこにありすぎたからか。一年を通じて豊かに色を変えるその平原は、無限の広がりを持ち、永遠の連続性を持つと、幼少期の私は素朴に信じていた。label: c4b67848-1278-4710-8a50-87347468b4df这里有我的村庄,我家的田地。这片景色养育了我。
label: cfeb7312-2065-4a78-9b7a-b6c8ff181d18或许是因为曾经这一切都过于理所当然,年幼时我曾天真地相信,这片一年四季色彩变化缤纷多样的平原无限广阔且会永葆活力。Locale 別にディレクトリを分け, ユニークなラベルを同じページに割り当てるような仕様としました.
ラベルの割り当ては開発用ツールの Formatter がついでに行うようにしています.
scenario: "00_00"labels: ["c4b67848-1278-4710-8a50-87347468b4df", "c4b67848-1278-4710-8a50-87347468b4df", ...]セーブデータはこのような構造で管理しています.
“現在のシナリオファイル” と “訪れたラベルの配列” を持っており, 現在の言語設定でディレクトリを解決後シナリオをロードし, 一致するラベルをたどり座標を特定しています.
ラベルを配列で管理することで, “言語の差により発生した新しいページ” も拾えるようになっています.
2025年2月
テーマ機能

色調がグレイスケールなのもあり, シーンごとにもう少し動きが出るとうれしそう という発想から, 漫画のコマ割りのような画面のレイアウトを定義できる “テーマ” という概念が実装されました
theme: three image: textures/bg/iconuroom/ir1.png location: イコヌの部屋
スクリプト中で自由にテーマを切り替えることができ, エディタで自由に配置やエフェクトを載せられるように, リリース時点では 50 個ほどのテーマが存在しています.
3D in 2D


3D in 2D の表現が簡単にできそうだったので, 転がっていたヘリコプターのモデルをタイトル画面の背景で回したりガビガビさせたり.
この時点では 3D 表現はタイトルだけに存在していましたが, この後本編にも採用されることになります.
Godot Engine での開発
Godot Engine を採用して良かった点をいくつか挙げます.
GDScript
動的型付けである部分は Type Hinting も用意されていたり, 型の曖昧さによる不便よりも, シンプルな文法で早く記述できたり, 実行中にコードを修正できる柔軟さといった利点の方が多く感じられました.
もともと Unity を使用していたため C# 版から GDScript 版へと移行して Godot を使いましたが, C# 版は同じ内容でも 1.5 ~ 2 倍ほど記述量が増えていたように思います. このプロジェクトでは特に C# 版を使用する制約もなかったため, 大きな問題はありませんでした (個人的には Interface や nullable が無い部分の厳しさはあるのですが, この二点くらいです).
Unity では Cysharp/R3 (UniRx) をかなり使っていたので, 開発中に minami110/godot-signal-extensions という似たような文法で購読管理ができるプラグインを公開しました.
エラーハンドリングの仕組みが GDScript 側にほぼ用意されていないので, OnError, OnCompleted をオミットするという割り切った仕様にしています.
テストには MikeSchulze/gdUnit4 を利用しています, MikeSchulze/gdUnit4-action という GitHub action も用意されているので, CI/CD に自動テストを組み込んでいます.
使用していたプラグインはこの2つのみで, 特に機能不足を感じることもなくリリースまで行うことができました.
シンプルな機能
エンジン側に用意されている機能自体がシンプルなため, 公式ドキュメント がめちゃくちゃ読みやすく, 理解しやすいです.
Godot 3 -> 4 にかけて Visual Scripting が切り捨てられるという判断がなされたり, 今後バージョンアップを重ねてもコアの機能が肥大化しなさそうな安心感があります.

Layer はすべて空で, Water Layer は存在しません. .ttf や .otf をそのまま使用することができます.
OSS
OSS (MIT) のため ソースコードが読めますし, 開発に参加することができます, もちろん無料で使えるということも大きいです.
この記事の初めにある写真は, 昨年注文していた Godot 公式のぬいぐるみです.
現在 V2 のプレオーダーが実施中で, Godot Foundation への支援も兼ねているようです.
Godot Robot V2 Plushie | Makeship
おわりに
この後も2ヶ月ほどはちまちまとしたプラットフォーム対応や最適化. UX 調整を進めていましたが, かなり地味な内容になってしまうので一度ここでこの記事は終わりとさせていただきます.
次回は, LM7 による 3Dモデリングに関する内容を予定しています.