Textil で UE のバージョン管理を本気でなんとかする
この記事は、Unreal Engine プロジェクトが抱えるバージョン管理の課題に苦しめられ続けてきた私が、理想を追い求めたツールを開発することにし、そして形になりつつある Textil というツールについての説明です。 この記事には #PR を含みます。
UE を取り巻くバージョン管理
Unreal Engine を取り巻くバージョン管理の環境は、過剰に複雑です。
お金で解決できる大規模スタジオならまだしも、インディープロジェクトや個人開発で「ちょっと快適にバージョン管理したいな」と思っただけで、考えなければいけないことが両手の指では収まりません。しかも、その問題は UE プロジェクトの構造や、利用するバージョン管理ツールに精通していないと解決できないような領域にまで踏み込むことがあります。
これは深刻な問題だと思っています。私は Web やインフラ、ツール開発といった外の業界のプロジェクトにも関わってきましたが、ただプロジェクトを管理するというだけでこんなに悩まなければいけないのは大変稀です。そして、その結果として UE プロジェクトの開発現場では必然的にレガシーな選択が取られがちです。柔軟性と利便性、コミュニケーションコストを犠牲にして、安定を取る。
お金で解決する道
たとえば Perforce。安定していて実績もありますが、ライセンス費用は安くないし、サーバーの構築・運用も必要です。分散開発には向かず、ブランチ運用も Git ほど気軽ではありません。「みんなで同じサーバーに繋いで、同じブランチで作業する」という、ある意味シンプルだけど制約の多いワークフローに落ち着きがちです。
Git で頑張る道
一方で Git を選ぶと、今度は別の問題が出てきます。
Git 自体は素晴らしいツールです。世界で最も使われている VCS であり、UE や周辺のゲーム業界などを除けば、Git 以外を使っている現場を見つけるのは難しいほどです。この普及率による利益は凄まじく、現代的なプロジェクト管理ツールや CI/CD ワークフローなどは皆 Git をまっさきにサポートします。
しかしながら、UE との相性には課題があります。
- バイナリファイルはマージできない
- .uasset のようなバイナリファイルはマージできません。2人が同じファイルを編集してしまうと、どちらかの変更を捨てるしかなくなります。Git LFS を導入すれば大容量ファイルは扱えるようになりますが、
.gitattributesの設定、チームメンバー全員への LFS インストール、転送量に応じた課金、ロック機能の限界......と、「普通に Git を使う」だけでは済まない設定の複雑さが待っています。
- .uasset のようなバイナリファイルはマージできません。2人が同じファイルを編集してしまうと、どちらかの変更を捨てるしかなくなります。Git LFS を導入すれば大容量ファイルは扱えるようになりますが、
- ロック機能の限界
- Git LFS のロック機能(
git lfs lock)も、ブランチ開発との相性に根本的な問題があります。ロックが空いていても、別ブランチで既に編集されているかもしれない。マージ時に初めて競合に気づく、という事態が起こりえます。
- Git LFS のロック機能(
- 転送量コスト
- Git を使うなら GitHub で管理したいと思うでしょうが、ここで立ちはだかるのが LFS 料金問題です。GitHub は保存料金だけでなく LFS ファイルの転送にも課金しており、クローンが重なったり CI/CD で頻繁に転送が走ったりすると、小規模プロジェクトでも簡単に月数百ドルの請求に達することがあります。
- アーティストにとっての Git
- Git はエンジニアにとっては馴染み深いツールですが、アーティストにとってはそうでもありません。コマンドラインが前提の操作体系、「ステージング」「コミット」「プッシュ」といった概念、.uasset を変更しても何が変わったのかテキスト差分では分からない。TortoiseGit や SourceTree といった GUI ツールもありますが、これらはテキストファイルの管理を前提に設計されています。
どれを選んでも
Plastic SCM(現 Unity Version Control)など、他の選択肢ももちろんあります。しかし結局のところ、何を選んでも UE プロジェクトという特殊な管理対象であるがために、痛みを避けては通れないというのが現状だと思っています。
Textil という解決策
先ほど「Web やインフラ、ツール開発といった外の業界のプロジェクトにも関わってきた」と書きました。そして、ちょっとだけですが UE の内部についても知識がありますし、UE プロジェクトの現場の痛みも知っています。
......じゃあ、この問題を解決するツールとプラットフォームを開発してしまえばいい。
ということで、私たちは Textil というソフトウェアの開発を始めました。Git をベースにしながら、UE プロジェクト特有の課題に正面から取り組むバージョン管理ツールです。既存の GitHub リポジトリにそのまま接続でき、Git のワークフローを維持しながら、バイナリファイル、特に .uasset 管理の痛みを解消することを目指します。
- ブランチ開発でも安全なロック
- Git LFS より高速なファイル転送
- 転送量無料のクラウドストレージ。かかるのは保存料金だけ
- .uasset のプレビューと差分表示
- Unreal Engine との高度な連携
- etc...
「Git の自由さと、バイナリ管理の確実さを、すべての Unreal Engine 開発者へ」。これが Textil のコンセプトです。
細かい話の前に
まずはこの画面を見てください。
.uasset プレビュー
Blueprint のグラフが見られます。Material も、Control Rig も。(タブで画像を切り替えられます)

EventGraph、ConstructionScript などのグラフをそのまま表示

EventGraph、ConstructionScript などのグラフをそのまま表示
Material Instance のパラメータ、Texture アセットや Data Asset もこの通り。

パラメータオーバーライドの一覧を表示

パラメータオーバーライドの一覧を表示
また、エンジンのリフレクションシステムと連携することで、専用のプレビューをサポートしていないアセットであっても、ある程度内部のデータを確認することができます。
しかも差分が見られる
プレビューだけではありません。差分表示にも対応します。
もちろん、エンジン標準の Blueprint 差分ツールより高機能です。変更のあるノードだけをハイライトしたり、ロジックに影響しない見た目だけの変更をフィルタリングしたり。

変更・追加・削除されたノードを色分け表示

変更・追加・削除されたノードを色分け表示
OFPA にも対応
One File Per Actor(OFPA)を有効にしたレベルでは、各 Actor が個別の .uasset ファイルとして保存されます。ファイル名はハッシュ値になるため、通常の Git クライアントでは何が何だか分かりません。
Textil では、ファイルの中身を解析して Actor 名を表示します。将来的には配置元の Actor アセットまで辿って、よりよい表示を実現する予定です。

ハッシュ値のファイル名ではなく、配置した Actor 名で表示
このように、エンジンと密接に連携することで、リフレクションや .uasset を理解し、UE 開発に差分に基づく快適なバージョンコントロールをもたらそうと目論んでいます。
バイナリ管理問題とどう向き合うか
しかし、問題は .uasset が UE の外で扱いにくいということだけではありません。ここで改めて課題を整理しましょう。
ファイルロック
ファイルロックは「編集前に宣言する」ことでバイナリ競合を回避する仕組みです。Git LFS にもロック機能(git lfs lock)がありますが、先述のとおりブランチ開発との相性に根本的な課題があります。
具体的に見てみましょう。Alice が main ブランチで Hero.uasset を編集し、コミットしてロックを解除したとします。その後、Bob が feature ブランチで同じファイルをロックしようとする。ロックは空いているので、当然取得できます。Bob は何も知らずに編集してコミット。そして feature を main にマージしようとすると......競合。どちらかの変更を捨てるしかありません。
従来のロックは「今誰が持っているか」しか見ていないのです。
大量 × 大容量バイナリファイル転送
「Git LFS は遅い」と言われることがあります。たしかに、Plastic SCM などのファイル転送と比べると、同じ回線速度のはずでも Git LFS の Push / Pull は遅いことがあります。これはなぜでしょうか?
原因のひとつは、Git LFS が Git の Filter 機構を経由していること。Git 本体と LFS プロセスの間でファイルごとにやり取りが発生するため、ファイル数が多いほどオーバーヘッドが積み重なります。そもそも、Git に Filter で呼び出されないとアップロード/ダウンロード処理が走らないので、LFS 側ではバイナリファイルの処理の分配やスケジュールを調整することができません。すると、帯域は空いているのにファイルを転送していない時間などが生まれてしまいます。
また、サーバーによっては何も考えずに LFS が構築されており、コネクションが HTTP/1.1 で行われていることもあります。すると、1つの接続で1つのリクエストしか処理できないため、大量のファイルを送るにはコネクションの確立と切断を繰り返すことになります。実はこうした転送レイヤの問題すら絡んでいるのです。
そして先述のとおり、GitHub LFS では転送量に応じた課金も発生します。速度だけでなく、コストの面でも悩ましい問題です。
Textil Cloud
これらの問題を解決するため、Textil は独自のプロトコルでクライアントと連携する Textil Cloud を提供します。
ロック
ファイルの「系譜(Lineage)」を追跡することで解決します。ロック取得時に、申請者が最新の状態を持っているかを検証し、持っていなければ拒否。先ほどの例でいえば、Bob がロックを試みた時点で「Alice の変更を取り込んでいません」と怒られます。Bob が git merge main で Alice の変更を取り込んでから再度試みると、今度は成功。「ロックを取れたなら、安全に編集できる」ことが保証されるわけです。
ちなみに、この検証にはファイル内容のハッシュを使っているので、squash merge や rebase でコミット履歴が書き換わっても正しく追跡できます。ロック状態は WebSocket でリアルタイム共有されるので、誰かがロックを取ると即座に全員に通知が届きます。詳細はドキュメントをどうぞ。
ファイル転送
Filter 機構をバイパスして独自のパイプラインで処理します。プロトコルには QUIC を採用しており、単一コネクション上で複数ファイルを同時に流せるので、帯域を無駄なく使い切れます。転送が中断しても途中から再開できるので、不安定な回線でも安心です。
そしてなんと、Textil Cloud では転送量は無料です。かかるのは保存料金だけ。しかもその保存料金もかなりの格安に設定しています。CI/CD で何度クローンしても、チームメンバーが増えても、転送量を気にする必要はありません。
Textil Cloud × GitHub
「え、じゃあ Textil を使うには専用のクラウドじゃないとだめなの? GitHub は使えないの?」と思ったかもしれません。
安心してください。そんなことはありません。
Textil Cloud が担うのは、バイナリファイルの高速な転送・保存と、ロックの管理だけです。プロジェクト自体は GitHub で管理しつつ、バイナリだけを Textil Cloud に逃がす。こうすることで、Git のエコシステムに乗りながら Textil のバイナリ管理システムを利用できます。
また、通常の Git LFS との互換動作も保証します。そこまでの高速転送やロックシステムはいらないという方は、GitHub の LFS だけで利用していただくことも可能です(でも、プロジェクトサイズがちょっとでも大きくなってきて、転送もかさむようになってきたら、GitHub LFS より多分、いや絶対 Textil Cloud のほうが安いということは覚えておいてください)。
セキュリティ
プロジェクトデータやロック情報、メンバーデータを外部サービスに預けることに対して、セキュリティ上の不安を感じる方もいるでしょう。特にゲーム業界では、発売前のアセットが流出することは致命的です。私たちはその懸念を理解しています。
一般的なマルチテナントサービスでは、ひとつのストレージやデータベースに複数の組織のデータが混在し、ソフトウェアのアクセス制御で分離します。実装経験のある方なら、このアーキテクチャに馴染みがあるかもしれません。しかし、この方式ではアプリケーションにバグがあった場合、他の組織のデータにアクセスできてしまうリスクがゼロではありません。
Textil Cloud では、組織を作成すると、その組織専用のストレージバケット、データベース、処理ワーカーが確保されます。組織固有のデータはすべてこの専用リソースに保存され、組織リソースへのアクセスは上位のルーティングシステムで振り分けられます。つまり、そもそも他の組織とはデータの保存先や処理の実行場所が隔離されるので、たとえ私たちの実装に不具合があったとしても、アクセス権のないユーザーが他の組織のデータに誤ってアクセスすることは、インフラレベルで不可能な構造になっています。
Textil CLI
目ざとい方は、「専用のプロトコルを使うということは、Textil Cloud を使ったら Textil のアプリからしかリポジトリにアクセスできなくなるのでは?」と思ったかもしれません。
それもご安心ください。
私たちは Textil の機能をすべてライブラリとして開発しています。GUI アプリケーションは、そのライブラリを使って作られた一つの実装に過ぎません。そして、まったく同じ機能をコマンドラインで提供する Textil CLI も、GUI と同時に提供します。
これが何を意味するかというと、CI/CD パイプラインから Textil CLI を呼び出せば、認証や独自プロトコルへの対応が済んだ状態で Textil Cloud 上のデータにアクセスできるということです。GitHub Actions でも、Jenkins でも、お好みの CI 環境で Textil の恩恵を受けられます。
Unreal Editor プラグイン
アーティストやレベルデザイナーにとっては、エディタから離れずにバージョン管理できることが重要です。
Textil は、Unreal Editor 向けの専用 Source Control プラグインも提供します。エディタ上でファイルのロック状態をリアルタイムに確認でき、コンテンツブラウザから直接ロックの取得・解除が可能です。誰かが別のマシンでロックを取得すれば、それが即座にエディタ上にも反映されます。
わざわざ外部ツールを開かなくても、いつもの作業フローのままで Textil の機能を使えるようにする。これも、UE 開発者にとって使いやすいツールを目指す私たちの取り組みのひとつです。
もちろん、Textil の UE プラグインはただ UE の Source Control 機能を通して操作できるだけではありません。Textil のアプリケーションと相互に連携して動作することで、Textil から UE のコンテンツにジャンプしたり、エディタから Textil の差分画面を呼び出したりといった機能も提供予定です。Textil と Unreal Editor の間をシームレスに行き来できる体験を目指します。
クローズドテストのお知らせ
Textil は現在クローズドテストを実施中です。
まだまだ開発中で、この記事では紹介しきれていない構想段階の機能も多数あります。正直なところ不安定なところも多々ありますが、だからこそ、実際に UE プロジェクトを開発されている方々からのフィードバックをいただきたいと思っています。
「UE のチーム開発でこんなことに困っている」「こういう機能があったら嬉しい」「ここが使いにくい」といった声を、ぜひお寄せください。一緒に、UE 開発者にとって本当に使いやすいツールを作っていけたらと思っています。
- Textil 公式サイト: textil.dev
- クローズドテスト申し込み: textil.dev/apply
おわりに
UE × Git のバージョン管理は、長らく「仕方ない」と受け入れられてきた領域だと思います。LFS の設定は面倒だし、バイナリは競合するし、ブランチ切り替えは重いし。
でも、仕方なくはないはずです。Git の柔軟性を維持しながら、バイナリファイルの管理をもっと快適にできるはず。それが実現されれば、現代的なバージョン管理や CI/CD のエコシステムが UE コミュニティにもたらされ、私たちの開発体験が向上するのではないでしょうか。Textil はその可能性を追求します。
まだまだ開発中のツールですが、同じ課題感を持つ方々と一緒に、より良いものにしていけたらと思っています。
メリークリスマス🎁
