[Unity] #13 : VR 実験で踏み抜いた Prefab 地獄と Git 復旧失敗の記録

はじめに

今日は、今までコツコツと積み上げて作成してきたプロジェクトが壊れるというショッキングな出来事があり、その復旧にかなりの時間を要しました。

git に直前のバックアップを取っていたので何かあってもすぐ直せるだろうと思ってた事も災いを招いています。
実際、今回のケースではgitの復旧は役に立ちませんでした。

具体的には、Three.jsでも初期段階で実装したVRへの対応を、Unityでもプロジェクト規模が小さい間にやっておこうと、いつも通り早朝から開始。

当初は、Three.js で、THREE.Water or THREE.Water2 を実装する予定でしたが、VRの実装を優先して開始。

VR関連の公式>?VR系assetを導入するもいくつかエラーが出てコントローラーが出てこず、AIの指示を鵜呑みにしてキャッシュやライブラリ情報をざっくりと削除。

再起動して、自動でリカバリをするも、sceneは回復しましたが、プレイヤーが全く動かない…。

その事でAIの指示を聞いて修正するうちに状態が悪化。

AIの指示を鵜呑みにしてはダメだと、原因を探っていくと、VRM系のassetをgithubからダウンロードしてる事を思い出して、それが原因だと気づき手動インストールして移動は回復しましたが、 どう頑張っても、キャラクターが移動がカメラの向きにあわせて移動せず、↑キーで移動すると 常にワールド座標の北へ 移動し続ける修正ができず、AIの指示を聞いても全く直らない。

この事だけで3時間ぐらいトライアンドエラーを繰り返してたと思います。

私自身もまだまだUnityの初心者でカメラに関しては分からない事の方が多く、AIの指示に頼らざる得ないが、状況は悪化するばかり…。

検索しても同様の情報はあっても、バグ内容が違うので当てにならない。

結果的に、ChatGPT5.2を使う事を諦め、Gemini3 Proに聞いたところ10分で解決。

以上、簡単な今日一日の流れで、進捗はゼロといか、マイナスで、記事を書くのを辞めようかと思いましたが、今回は、失敗談として内容をまとめてみます。

0. この回のゴール

この記事では、2026/01/24 に発生した プロジェクト破壊事故の技術的詳細 をまとめる。 主観を排し、実際に起きた操作・Unity の挙動・失敗要因・復旧試行の内容 を時系列で記録する。

対象バージョン:

  • Unity 6000.3.2f1
  • GitHub(git + GitHub Desktop)
  • VRM / PlayerArmature を含む 3D プロジェクト

1. 発端:SimpleWater 実験 → XR / VR 設定へ脱線した流れ

想定していた作業

Plane + ShaderGraph に 時間で頂点を揺らす(sin波) + 時間で色を変える(UV・時間) という、Three.js の THREE.Water の 最小再現 を作るだけ。

数十分で終わるはずの軽いタスクだった。


実際に起きた流れ(正確に記録)

  1. SimpleWater の話 → XR / VR の話題に脱線 (“VR化するとこうなる”という説明の中で誘導が入る)

  2. Unity 6000 の XR 構成が自動化されている影響で OpenXR / Interaction Toolkit 系の設定を確認する流れに入る

  3. Project Settings の XR / XR Plug-in Management / XR Interaction Toolkit の設定を触る

  4. ただの水面実装のはずが、 プロジェクト全体の XR 設定が変わった状態で保存される

  5. これにより、 Scene 内の PlayerArmature(VRM)周辺がなぜか不整合を起こし始める

  6. Prefab / Scene のリンク状態が不安定になり、 Prefab Overrides のズレが増殖


✔ この「不整合」の特徴(実際に起きた症状)

  • PlayerArmature の階層がズレ始める
  • Animator が正常動作しなくなる
  • RootBone や HumanBone がズレる
  • Prefab と Scene の差分が常に残り続ける
  • Apply / Revert がどちらも意味を成さなくなる

ここで最初の破壊フェーズに突入した。

2. PlayerArmature が壊れた原因

破壊されたポイント(実際に起きた現象)

以下は 実際に壊れたもの をそのまま列挙する。

  • PlayerArmature(VRM アバター)本体の階層
  • Animator(アニメーションが動かない / 無効化される)
  • Humanoid 設定(HumanBone の割り当てがズレる)
  • SkinnedMeshRenderer の RootBone が書き換わる
  • Prefab Overrides が Scene 側と一致しなくなる
  • マテリアル(UniVRM → Standard へ化けるなど)
  • Scene側の修正が全て粉砕される

いずれも Prefab と Scene のリンクが破壊されたことによって連鎖して起きた。


決定的に壊れた操作:

「Overrides → Revert All」

Unity を触り慣れていないと 「元に戻す=正しい状態に戻る」 と誤解しやすいが、 実際の Revert All は以下の仕様になる。


✔ Revert All の“本当の挙動”

① Prefab を作成した瞬間の状態へ巻き戻す

→ つまり、VRM から変換した直後の“初期化されたアバター”に戻る。

② Scene 上で行った全ての調整は破棄される

  • 腰の回転調整
  • 肩の補正
  • 首の調整
  • マテリアルの最終修正
  • RootBone / HumanBone の修正 これらがすべて ゼロに戻る。

③ Prefab の内部構造が古い状態に戻るため、Scene 側と不整合が起きる

→ これが最悪のポイント。 Prefab と Scene が「違う階層構造」を持つ状態になる。

④ Prefab Overrides の “差分” が永久に消えない幽霊差分になる

Apply しても Revert してもずっと残り続ける =Prefab地獄の始まり

⑤ VRM 特有の Humanoid / マテリアル設定がリセット

UniVRM が行った変換は “Prefab 化したあと” の修正扱いになる。 → Revert All により全部破壊される。


✔ 結果として起きた最大の損害

数時間かけて調整した 腕・首・腰・RootBone・Material・階層構造 のすべてが完全消滅。

さらに、

Prefab と Scene の階層が“別物”になったため、 Apply も Revert も一切効かない「不整合状態」に突入。

ここから何をしても復旧できなくなった。

3. Prefab / Scene のリンク関係が崩壊

今回の事故の中で、 最も致命的だったのが “Prefab / Scene リンクの崩壊” である。

Unity 6000 系は Prefab の階層構造と meta/GUID を厳密に扱うため、 一度構造がズレると 内部的には“異なるPrefabとして扱われる”。 この状態になると、Apply / Revert のどちらも機能しない。


具体的に起きた挙動(事実だけ)

以下は、実際に起きた現象をそのまま記録したもの。

1. Prefab と Scene の差分が常に “1” のまま消えない

一見すると細かな修正の差分に見えるが、 内部的には階層構造が一致していないため、 差分が永続化した「幽霊 Overrides」になる。

2. Apply しても Revert しても一致しない

本来:

  • Apply → Scene を正として Prefab に書き戻し
  • Revert → Prefab を正として Scene を巻き戻す

だが、構造がズレている場合は どちらも成立しない。 Unity は「適用できる差分」だけ処理し、 構造そのものは無視される。

その結果、

  • Revert → 実質何も戻らない
  • Apply → Prefab に破損データを上書き のどちらかになる。

どちらでも復旧しない。

3. 本来親子にあるべき階層が Scene だけでズレる

例:

PlayerArmature
 └─ Armature
      └─ Hips

のような階層が、

PlayerArmature
 └─ Armature (Scene専用の偽物)
     └─ Hips

といった形で Scene 側だけ “別オブジェクト” が生成される。 GUID が一致していないため、Apply も Revert も効かない。

4. Prefab 側の修正が Scene に反映されない

Scene の階層がすでに分岐した「別実体」になっており、 Prefab → Scene の同期が壊れている。

5. デフォルト値が勝手に復活する

RootBone、Center、Head、Material などが Prefab 側の古い状態 に戻り続ける。

6. Material が Missing になる

VRM のマテリアルは 「UniVRM が生成する追加アセット → Prefab が参照」 という構造のため、階層破壊が起きると Prefab と Scene が別 GUID を指し始める。

結果: Scene 側だけ Missing になる。

7. SkinnedMeshRenderer の RootBone が勝手に変わる

階層がズレているため、 Unity が “一致する Transform を自動で推測” し始める。 この自動補完が暴走し、意図しない RootBone に切り替わる。


✔ 技術的に見た今回の崩壊の本質

今回の地獄の根本は “Prefab と Scene が別系統のオブジェクトになったこと”。

Unity では:

階層構造の不一致 = GUID が一致しないため別物扱い

となる。

その結果:

  • Prefab Overrides が永続する
  • Apply / Revert が効かない
  • Material / RootBone が勝手に変わる
  • Scene と Prefab の同期が永久に取れない

という “復旧不能の状態” に入る。

4. 破壊された状態での復旧試行(6時間)

Prefab / Scene のリンクが崩壊した状態で 復旧に向けて行った操作の全記録 を残す。 結論として、Unity 6000 系の Prefab 内部仕様のため どの手段も復元に繋がらなかった。


試したこと一覧(すべて失敗)


① Prefab の Revert / Apply

→ どちらも階層ズレが直らず → Animator が壊れたまま

Unity 6000 系は Prefab の階層構造(Transform GUIDの並び) を “ファイル構造として厳密に保持” している。

今回の破壊では:

  • Scene 側が 別 GUID の階層 を持つ
  • Prefab 側が 古い GUID の階層 を持つ

この状態のため、

  • Revert → Scene の偽オブジェクトが無視される
  • Apply → Prefab に破損階層が書き戻される

どちらも 構造不一致なので復旧しない。

Animator は RootBone の参照先が壊れているため 再生できない。


② Scene 側を正として Apply

→ Prefab 側の骨構造がおかしくなる

Scene 側は「階層がズレた“別物”」になっているため、 Apply すると Prefab 側に破損データを上書きする。

結果:

  • Prefab 側の HumanBone が全てズレる
  • RootBone が Scene 側の誤った階層に更新される
  • Prefab の階層そのものが破壊される

復旧どころか破壊が拡大する。


③ Prefab 側を正として Scene を削除 → 再ドラッグ

→ Prefab 自体が破損しているため意味なし

通常ならこの方法で「Prefab 正 → Scene に戻す」が成立するが、 今回のケースでは Prefab 側の GUID と構造が既に破損済み。

ドラッグしても:

  • 階層が元に戻らない
  • RootBone が不正のまま
  • Material が Missing
  • Animator が無反応

Prefab そのものが壊れているため、復旧は不可能。


④ Library フォルダ削除(再インポート)

→ フォルダ再生成はするが根本修正にならず

Library を消しても Scene / Prefab の GUID は meta に残る。

そのため:

  • broken GUID ←→ broken GUID
  • 不整合のままそのまま復元される
  • VRM のコンバート情報は再生成されない

Library 再構築は アセットの再インポートだけ であり、 今回のような 階層破壊・Prefab内部破損には効果ゼロ。


⑤ Animator Controller の再割り当て

→ RootBone のズレが残るため無意味

Animator そのものは割り当て直せるが、 Humanoid の「ボーン構造そのもの」が壊れているため:

  • Animator Controller を直しても → 骨が一致しないのでアニメーションできない
  • Avatar Definition も壊れているため → Unity が “推測して” 勝手に別のボーンを割り当てる → さらに破壊が進む

根本原因と関係ない。


✔ 6時間試しても復旧しなかった理由(技術的な本質)

Unity 6000 系の Prefab は 階層構造(Transform GUID)で全てを同期する仕様。 一度この GUID がズレると:

  • Prefab と Scene が“別オブジェクト”扱い
  • Apply / Revert のどちらも成立しない
  • Animator の骨構造も一致しない
  • Material 参照も一致しない
  • 見た目が同じでも内部は別物

つまり、 “復元可能な状態” を過ぎたため、何をしても 1mm も改善しなかった。

5. Git を使った復旧も無意味だった理由

Unity プロジェクトは一般的なコードベースとは違い、 ファイル同士の GUID(metaファイル)参照 によって構造が成立している。 今回の事故では、この GUID の崩壊 が起きていたため、

Git による “巻き戻し” は一切機能しなかった。


Git 上の問題(実際に起きた事実)

Unity プロジェクトは meta ファイルが全て

Prefab / Scene / Material / Animator / Avatar これらはすべて .meta の GUID でリンクしている。

1つ壊れると、 参照先が一致せず“別物扱い”になる。


Scene / Prefab は YAML だが 階層構造が複雑すぎて差分が意味を成さない

とくに VRM の階層は 100〜300ノード規模。 yaml 上では構造を認識できず、 壊れた行だけ直す という概念がない。


破壊された状態で commit 済み

破壊後の meta / Prefab / Scene が Git に保存されており、 戻すにも「壊れた GUID 套(カタマリ)」が巻き戻されるだけ。


GitHub Desktop で戻しても GUID マッピングが一致しない

Unity の GUID は ProjectSettings / Library / meta の複合状態 で成立する。

Library を再構築した時点で GUID の参照解決がバラけているため、 Scene も Prefab も一致しなくなる。


VRM の変換プロセスは再現不可能(インポート順に依存)

UniVRM は

  1. VRM ファイルを読み込む
  2. 内部で Humanoid 定義+マテリアルを生成
  3. 必要に応じて追加アセットを自動生成
  4. GUID を付与

という 一度きりの処理 を行う。

Git ではこの“生成順序”が復元できないため、 VRM の階層構造が元に戻らない。


結果:Git の戻しでは復旧不可能だった

Git の巻き戻しでは、以下の理由で 正しい状態に帰れなかった。

1. 壊れた状態で meta が上書き済み

meta の GUID は壊れたまま履歴に残ってしまっている。

2. Library を再構築しても GUID 衝突で階層が戻らない

Library はアセット再インポートにすぎず、 構造破壊された Prefab との整合性は戻らない。

3. VRM コンバートの一度きり処理が Git で再現不可

UniVRM の自動生成ステップが復元されないため、 Prefab も Scene も“似た別物”として扱われる。


✔ 技術的結論

Unity(特に VRM を含むプロジェクト)は、

Scene・Prefab の構造破壊が起きると
Git の巻き戻しでは復元できない。
正常な GUID のついた“完全バックアップ”のみが救済手段。

これはソフトウェアとしての Unity の限界であり、 今回の事故はその典型例となった。

6. 2時間ダウンの後、唯一の解決は外部AI(Gemini)だった

Prefab / Scene の階層破壊が進行した状態では、 Unity の標準手段(Apply / Revert / Reimport / Library 再生成 / Git 巻き戻し)では 一切復旧できない ことが6時間の試行で判明した。

精神的に限界を超えて2時間ダウンし、 その後冷静になって外部AI(Gemini)に相談したところ、 以下の“唯一の現実的解決”が提示された。


外部AIにより得られた判断

###1. 「この Prefab は既に修復不可能である」

Gemini の判断では、 Prefab / Scene の GUID 崩壊・Humanoid階層破壊・Material参照消失が 複合的に起きており、 Unity の機能では復旧不可の状態 と診断された。

これは、実際の6時間の試行内容とも一致している。


###2. 解決策は “壊れる前のバックアップを別名でインポートする”

具体的には:

  1. 壊れていない PlayerArmature(VRM → Unity化済)の 古いバックアップフォルダをコピー
  2. プロジェクトの Assets/_Work/ など別ディレクトリに 別名(例:PlayerArmature_Backup)でインポート
  3. Scene に配置し直す
  4. Animator / RootBone / Material を再リンク
  5. 問題ないことを確認したうえで、 旧Prefab(破損しているもの)を完全削除

これにより、 破損した Prefab とは完全に切り離された “新しい健全な階層” が確立できた。


この方法が成功した理由

Unity の Prefab / Scene のリンクは GUID と階層構造の一致 に依存している。

破損Prefabは:

  • GUID が壊れており
  • Scene と階層が一致せず
  • Humanoid の RootBone が崩壊し
  • Material が Missing

という再生不能の状態 になっていた。

ゆえに、

“壊れたPrefabを直す” ではなく “健全だったPrefabそのものを別名で再導入する”

という方法だけが成立した。

これは Unity 6000 系の仕様から見ても合理的な選択である。


結論

今日の6時間は単なる徒労ではなく、

「Unity の Prefab / Scene は破壊が一定ラインを超えると 修復不能になる」という事実を見抜いた日

だった。

そして、

復旧手段は「壊れる前の完全バックアップを別名で再導入する」しかなかった。

7. まとめ:今回の技術的ポイント

✔ XR設定は水面実装と完全に無関係

SimpleWater(ShaderGraph)と XR 設定は依存関係がなく、 XR/XRI/OpenXR を触ったことで プロジェクト全体の Player 設定・マネージャ構成が変わり、 Scene 内のオブジェクトに副作用が出た。 水面実装時は XR 領域には触れない方が安全。


✔ PlayerArmature(VRM)は Revert All で完全破壊される

VRM → Unity 化 → 調整済み の Armature は、

  • Humanoid のボーン再割り当て
  • RootBone 修正
  • マテリアル変換(UniVRM)
  • プロポーション調整
  • 階層の微修正

など、Scene 上で施した多くの処理が “Prefab 化後の差分” として扱われる。

そのため Revert All = 調整全消し+古いPrefabへの巻き戻し になり、 骨構造・階層・Material がすべて崩壊する。


✔ Prefab / Scene のズレは一定ラインを超えると復旧不能

Unity の Prefab 同期は GUID と階層構造が完全一致して初めて機能する。

今回の事故では、

  • Scene 側の階層が “別 GUID” の偽物に変化
  • Prefab 側は古い階層に巻き戻り
  • RootBone と HumanBone が不一致
  • Material 参照も別 GUID に分岐

この状態になると、 Apply も Revert も、Unity 内部ロジックが適用不能になる。

= 人力での修復は原理的に不可能。


✔ Git は Unity の rootbone / 階層破壊にはほぼ無力

理由:

  1. 壊れた meta(GUID)が履歴に残ったまま
  2. Library 再生成では階層の整合性は戻らない
  3. VRM の変換処理(自動生成アセット)は “一度きり” で Git では再現不能
  4. Scene / Prefab の YAML 差分は階層破壊の修復に使えない

そのため、 Git の巻き戻し=壊れた GUID 帯の復元にしかならず、 破損前の正常動作には絶対戻らない。


✔ 最終的な解決は「壊れる前の完全バックアップ」を再導入するしかない

Prefab / Scene の構造が壊れた場合、 Unity の仕組み上、 “破損後のファイルを修正して直す” ことは不可能。

有効なのは以下ののみ:

  • 破壊前の PlayerArmature(VRM → Unity 化済)を 別名でインポートして新規として扱う
  • Scene に再配置
  • Animator / RootBone / Material を再リンク
  • 壊れた古いPrefabを完全削除

おわりに

defaultで最初に設置されているカメラは、シーンに固定されており、複雑な動きをする事がなく制御が楽ですが、 FPSっぽいカメラを実装する場合、現状ではStarter Assetsに付属するカメラと、FreeLook Cameraを入れ子状態で実装していて、 もし問題が発生した場合、何処に原因があるかというのが非常にわかり辛く、ChatGPTに聞いても全く解決せず、AIが憶測であれこれ設定を提案し、何度も修正変更するも全く回復しませんでした。

仕方ないので一度消してゼロから新たにStarter Assets を導入して、アバターも再設置して試すもやっぱり動かない。

これにはほんとに参って心が折れそうでした。

何時間、格闘したか分からないです。

尚且つ厄介なのは、consoleにはエラーを一切表示しない事。

ChatGPTに聞いたのでは無理だと悟って、Cemini3 Pro に聞くもやはり原因が分からず解決はできないまま。

大元のStarter Assets のスクリプトに原因があるのではないか?とGemini3 Proの提案で、コードを手動で何度か修正。

それでもやっぱり直らない。

設定はすべてあってるので動くはずなのにおかしいと…。

デバックログを出力するコードをGemini3 が提案して、実行結果をログへ出力すると、そのログの1つに答えが・・。

削除したはずのカメラが残っていて、それを拾っていたことが原因で、これはホントにログを出力しないと分からなかったと思います。

尚且つ、GUIで変更する必要があり、尚更わかりにくい。

直った瞬間に、肩の力が抜けて、ただ、昨日実装した状況に戻っただけで、進捗ゼロでもう実装する気力は残ってなかったです。

なので、今日は失敗談としてこの記事を残して、終わりにしたいと思います。

自分で書いたコードには多数のデバックログ出力コードを書いてるのですが、配布されているアセットはエラーログしか吐かないので、今回の件で、配布スクリプトのデバッグも場合によって必要なのだと、実感しました。

Three.jsなどは、すべてをコードで制御するので間違いがあってもコードを読めばわかるのですが、Unityの場合は、インセプターでGUI操作も必要なため、そこが分からないとほんとどうしようもない状況に陥ったりするので構造を理解してないと、無限の迷宮へ迷い込んで抜けられなくなります。

UnityとThree.jsは一長一短ですね。

私は、Three.jsの方が好きかも、ただ、実装は物凄く大変だし時間もかかりますが。