[Unity] Prefabのコンフリクトを解消する


大前提として

Unityのチーム開発において、複数人が同じシーンやPrefabを別々に編集することは避けるべき、とされています。
Prefabファイルの実体はテキストファイルではありますが、決して人間が読みやすい形とは言えず、コンフリクトが発生するとマージが大変だからです。

そもそもコンフリクトって何ぞやという方へ

英字では「Conflict」、直訳すると「衝突」です。

例えば以下のようなコードがあったとします

void hoge()
{
    Debug.Log("hogehoge");
}

ここへ、開発者Aが自身のブランチ内で以下のような変更を加えたとします。

void hoge()
{
    Debug.Log("hoge");  // hogeは一回言えばいいんだよ!
}

しかし同時に、開発者Bがこれも彼自身のブランチ内で以下のような変更を加えたとします。

void hoge()
{
    Debug.Log("fuga");  // hogeよりfugaにしようぜ
}

これらのブランチのどちらが先に本流のブランチに取り込まれたにせよ、後から発行されたマージリクエストではこれらの変更のどちらを採用すべきか機械的に判断できません。

これが「衝突」です。
人間がどちらを採用するのか、あるいは両方を採用するのか、その場合はどのような形にすべきなのか、指示してあげなければいけません。これを「コンフリクトの解消」と言います。

Prefabは本来人間が読むものではない、だからマージが面倒くさい

上記の例で示したような、人間が読むことのできるプログラムコードのコンフリクトはまだマシです。あるべき仕様の形になるようにそれぞれの変更を取捨選択できます。

しかしPrefabはUnityエディターからGUIで編集されるものであり、そのファイルの実体に何がどう書き込まれるかなど知るはずがありません。
実際のところ、PrefabもシーンもYAMLというマークアップ言語で各オブジェクトの内容が記述されています。しかしこれを直接編集して開発している人間はまずいないでしょう。

しかしいざコンフリクトが発生すると、直接YAMLファイルの衝突部分を見せられることになります。衝突が発生したファイルはまともな状態にはなっていないので、Unityエディターから調整することもできません。

ではコンフリクト解消のためだけに我々はPrefabのYAML構造を理解しなければならないのか?
その必要はありません。

UnityYAMLMergeという便利なものがある

Unity公式ツールです

Unityをインストールしていれば同梱されています。Unityが扱うYAMLファイルのコンフリクトを解消してくれる専用ツールです。

これを使ってPrefabを正しくマージできるよう、私の環境をセットアップしてみます。

導入対象環境

私の環境は以下の通りです。個人の使い方によっては設定すべき値が異なる場合があることにご注意ください。

  • OS: Windows 11 Pro

  • エディター: Visual Studio Code

  • バージョン管理ツール: Git

  • Gitクライアント: Visual Studio Code標準機能, Git Graphプラグイン

  • ターミナル: Ubuntu on WSL

UnityがWindows側にインストールされているため、VS Codeのリモート開発機能は使用せず、gitコマンドの実行やターミナルでの一括操作などにのみWSLを使っています。

UnityYAMLMergeがあるパスを取得する

まずはこのUnityYAMLMergeが存在するパスを調べます。すでに貼った公式ドキュメントにも書いてありますが、UnityHubを使ってUnityエディターをインストールしている場合は間にバージョン番号が挟まったりします。

私の環境ではこれでした。

C:\Program Files\Unity\Hub\Editor\2022.3.4f1\Editor\Data\Tools\UnityYAMLMerge.exe

Gitのマージツールを設定する

次に、マージに使用するツールとしてUnityYAMLMergeをGitに設定します。
今回は現在開発中のプロジェクトでのみ有効になるように、リポジトリの設定に加えました。

リポジトリ直下にある .git/config を編集します。以下の行を適当な位置に足してください。

[merge]
	tool = unityyamlmerge
[mergetool "unityyamlmerge"]
	trustExitCode = false
	cmd = '/mnt/c/Program Files/Unity/Hub/Editor/2022.3.4f1/Editor/Data/Tools/UnityYAMLMerge.exe' merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"

簡単に解説すると、「unityyamlmerge」の名前でマージツールを宣言し、その詳細を「mergetool "unityyamlmerge"」のブロック内で定義しています。

今回私がちょっとハマったのが、cmdに指定する実行ファイルへのパス書式です。

導入対象環境のセクションでも述べた通り、私はgitコマンドをWSLから実行します。つまりWindows環境ではありません。そのため、このcmdには「WSLから見たときのUnityYAMLMergeのパス」を指定する必要がありました。

友人の環境の場合

共同開発者である友人はWSLを使用しておらず、PowerShell上でgitコマンドを実行しています。その環境においては以下の設定で動作しました。

[merge]
	tool = unityyamlmerge
[mergetool "unityyamlmerge"]
	trustExitCode = false
	cmd = 'C:\\Program Files\\Unity\\Hub\\Editor\\2022.3.4f1\\Editor\\Data\\Tools\\UnityYAMLMerge.exe' merge -p "$BASE" "$REMOTE" "$LOCAL" "$MERGED"

コンフリクトを解消させてみる

では実際にコンフリクトを解消させてみます。Assets/Prefabs/hogehoge.prefabにコンフリクトが発生している場合、以下のコマンドを実行します。

$ git mergetool --tool=unityyamlmerge Assets/Prefabs/hogehoge.prefab

「--tool=unityyamlmerge」は指定しなくてもよいです。複数のmergetoolが定義されている場合には指定した方がよいでしょう。

うまくいくと以下のような出力が得られ、コンフリクトが解消されているはずです。

Merging:
Assets/Prefabs/hogehoge.prefab

Normal merge conflict for 'Assets/Prefabs/hogehoge.prefab':
  {local}: modified file
  {remote}: modified file
Conflicts:
Conflict handling:

最後にマージを完了する

コンフリクトをすべて解消したら、マージを完了させましょう。

$ git merge --continue

そもそもコンフリクトさせないようにすべき

冒頭でも述べましたが、本来はそうあるべきです。

複数人が一つのPrefabを編集するというのはつまり、そのオブジェクトが複数の機能を有しているからであって。
それはいわゆる単一責任の原則に背いていることになります。

それがあるオブジェクトのサブ機能であり、子オブジェクトが複数追加されるという場合もあるかもしれませんが(今回の私のケースはそれだった)。

分離できるものは分離して、変更の影響範囲が小さくなるようにしましょう。

この記事が気に入ったらサポートをしてみませんか?