設定画面中に外枠ボタンをロックする仕組みを構築しました。
Sceneをまたぐと機能しなくなる現象に見舞われ、外枠UIをすべてDDOL(DontDestroyOnLoad)にすることにより回避しました。
ヒエラルキー的にじゃっかん複雑になってしまったかもしれませんが、仕方なしです。
バックログはDDOLにはしない設計なので、これまた独自の方法で対応。
Sceneをまたぐとなると、機能的なことはそれなりに対応が増えるのだということが身に沁みました。
新規スクリプト UI_UILockController

外枠用ロックボタン「Sys_LockManager」を模して、新規に「UI_UILockController」を作成しました。
物理的ブロックを任意で決める必要があるためです。
バックログ設定中はバックログボタンだけ操作可能にする必要があります。
また、従来のロックボタンは操作不可にします。

■ 実装の全体像
本コントローラーは、既存の Sys_LockManager(全体管理)から独立し、
「各UIパネルが自律的に自身の状態(ロック/通常)を制御する」ことを目的としたコンポーネントです。
1. 三位一体の制御ロジック
このスクリプト一つで、以下の3つのレイヤーを同時にコントロールします。
・物理レイヤー:透明な板(physicalLockBlocker)を出し入れし、背後のクリック入力を遮断する。
・ボタンレイヤー:CanvasGroup.alpha を操作し、パネル全体を「薄暗く」して非アクティブ感を演出する。
・除外レイヤー:一時的に階層から退避させ、全体の透明化の影響を受けないように(Alpha 1.0を維持)する。
■ 運用上のキーポイント
1. 不透明維持の仕組み(Opaque Escape)
動作:ロック時に、対象オブジェクトを Canvas 直下に生成した _OpaqueEscapeRoot へ一時的に親子付けを変更します。
理由:CanvasGroup の Alpha 計算は子階層へ強制適用されるため、物理的に階層を外に出すのが最も軽量かつ確実な回避策だからです。
復元:ロック解除時に、元の親(parent)と並び順(SiblingIndex)を完全に復元します。
2. 設定の使い分け
インスペクター上のリストの使い分けは以下の通りです。
| リスト名 | 主な用途 | 挙動 |
|---|---|---|
| hideOnPhysicalLock | ロック中に消したいメインボタン等 | SetActive(false) |
| showOnPhysicalLock | ロック中だけ出したい「解除ボタン」等 | SetActive(true) |
| opaqueUnderLockList | 薄くなってほしくない重要な警告文・アイコン等 | 階層退避(Alpha維持) |

■ 注意点
1. ライフサイクルの管理
退避中の破棄に注意:オブジェクトが _OpaqueEscapeRoot に退避している最中にそのパネル(コントローラー)を Destroy すると、避難中のオブジェクトも一緒に消滅します。
解決策:パネルを閉じる際は、必ず SetLock(false) を呼んで「全員を元の場所に戻してから」破棄するように徹底してください。
2. レイアウトの安定性
Anchorのズレ:親子付けが変わる際、稀に RectTransform の位置が微修正されることがあります。
解決策:コード内で SetParent(…, true) を指定しており、世界座標を維持するようにしていますが、退避対象は「画面中央固定」や「絶対座標」に近い要素にするとより安定します。
DDOL対応と関連問題の解決
問題① SerializeField参照のMissing
原因:
シーン側のオブジェクトをSerializeFieldに登録していたため、シーンロードで破棄されMissingになっていた。
解決:
対象オブジェクト(ビデオ外枠UIすべて)をUI_Root配下に移動。コード変更なし。
問題② UI_Root配下のオブジェクトが全シーンで表示される
原因:
UI_RootがDDOLのため、子オブジェクトがシーンをまたいで表示され続ける。
解決:
UI_SceneVisibilityスクリプトを新規作成。
hideInScenesに非表示にしたいSceneのBuildIndexを列挙する方式。
複数オブジェクト対応。

運用上の注意:新しいSceneを追加した際、必要に応じてBuildIndexを追記する。
問題③ UI_UILockControllerのOnSceneLoaded内Apply()の競合
原因:
OnSceneLoaded内のApply()がUI_SceneVisibilityのSetActive制御を上書きしていた。
解決:
Apply()の呼び出しを削除。
問題④ Sys_LockManagerのDDOL未対応
原因:
Sys_LockManagerがシーン側にあり、DDOLに対応していなかった。
解決:
UI_Root配下に移動し、DDOL対応を追加。

問題⑤ Sys_BacklogManagerのLockController参照Missing
原因:
Sys_BacklogManagerはシーン側に置く設計(シーンをまたいでログを残さないルール)のため、DDOL配下のUI_UILockControllerへのSerializeField参照がロードをまたぐと切れる。
解決:
UI_Root配下にBacklogLockControllerという名前のGameObjectを作成しUI_UILockControllerをアタッチ。
Sys_BacklogManagerのAwakeでGameObject名指定で取得。

問題⑥ _OpaqueEscapeRootが大量生成される
原因:
UI_UILockControllerが複数パネルにアタッチされており、
opaqueUnderLockListが空でも_OpaqueEscapeRootを生成していた。
解決:
CreateEscapeRoot()にリストが空の場合はスキップする処理を追加。
全体を通じての設計ルール
| オブジェクト | 配置場所 | 理由 |
|---|---|---|
| UI_Root | DDOL | UI管理の親 |
| Sys_LockManager | UI_Root配下 | 全シーン共通のロック管理 |
| UI_UILockController | UI_Root配下 | ロック制御対象と同じく永続 |
| BacklogLockController | UI_Root配下 | バックログ専用ロック制御 |
| Sys_BacklogManager | シーン側 | シーンをまたいでログを残さない |
| UI_SceneVisibility | UI_Root配下 | シーンごとの表示制御 |

コメント