設定UI画面を管理するシステム【Unity6】

絶対調味ロジック

ここまで「バックログ」「セーブロード」を作成しました。
専用Canvas」が増え、「DontDestroyOnLoad設定」も個別設定で増えました。
ヒエラルキーの見た目もごちゃついてきています。
そして、まだ未実装コンフィグとして「音量設定」や「テキスト設定」などが残っています。

今後の複数のシステム設定画面の実装を踏まえて、いまのうちに、
一元管理」できるようなシステムが作れないかAiに相談してみました。
(そもそも何をどう管理するのか分かっていないので、完全に丸投げ状態です)

現状の全体把握

ヒエラルキー

ヒエラルキーを見ると、Canvasがすでに5枚ある。

Dynamic_Canvas  ← ノベル本体(Msg, Msg_UI等)
Backlog_Canvas  ← バックログ専用
SaveLoad_Canvas  ← セーブロード画面
Black_Canvas   ← 暗幕(FadeManager)
Dialog_Canvas   ← ダイアログ

DontDestroyOnLoad対象

DontDestroyOnLoad対象も複数散在している。

SaveManager(ルート)
SaveLoad_Canvas(ルート扱い)
Black_Canvas(ルート扱い)
Dialog_Canvas(ルート扱い)
ChapterManager(System_set以下?)

問題の核心

Canvas単位でSortOrder手動管理している。

Black_Canvas → SortOrder: 999
Dialog_Canvas → SortOrder: 200
SaveLoad_Canvas→ SortOrder: 100
Backlog_Canvas → SortOrder: 10(推定)

今は4枚で済んでいますが、設定画面・音量画面・メニュー画面が増えると番号管理が破綻します

設定UI画面の管理システム設計案

方針Canvas1枚に統一 + Sys_UIRoot + Sys_InputManagerで一元管理

つまりは、
・DontDestroyOnLoadがあるCanvasを1つのオブジェクトにまとめる:機能の一元化と見た目の整理
・まとめたものをDontDestroyOnLoad設定し、最終的にはタイトルSceneにだけの設置で済む設計。
・各設定UI画面での入力キー・ボタンの制御を統一化。
・将来は、各設定UI画面の重なりの順番を自動化。(ソートを手動で設定しないで済む)

Canvas構成

ルート・DontDestroyOnLoad
SaveManager       ← ゲームデータ系・既存ルール通り
UI_Root(Canvas 1枚)  ← UI系を全部ここに統合(Sys_UIRoot.cs
 ├ Sys_InputManager.cs  ← 入力ブロック管理
 ├ Sys_FadeManager.cs  ← 既存・移動
 ├ Black_Screen
 ├ SaveLoad_Panel    ← 既存・移動
 └ Dialog_Panel      ← 既存・移動

シーン内
System_set
 ├ CutManager
 ├ ScreenshotManager
 └ ChapterManager  ← ゲームデータ系・シーン内で十分
Backlog_Canvas    ← シーンリセットでOKなので

シーン外
UI_PanelBase.cs  ← 置いておくだけの設計図

Sys_UIRoot.cs

DontDestroyOnLoad(Scene共有設定)のCanvasを一元管理するスクリプト。

Sys_InputManager.cs

入力ブロック管理(カウンター方式)
  Block() / Unblock() / IsBlocked

パネル開閉管理
  パネルが開くたびにSortOrderを自動で最前面にする【将来対応
  → 番号の手動管理が不要になる

パネル登録
  UI_PanelBaseを継承したパネルが自動登録される

UI_PanelBase.cs

・全UIパネルの基底クラス
・Open() → UIManagerにBlock()を通知 + SortOrder更新
・Close() → UIManagerにUnblock()を通知
・継承するだけで各パネルが自動登録される

UI_PanelBase.csはどこにも直接アタッチしない。
継承したスクリプトにOpen()/Close()の共通処理が自動的に組み込まれる設計図

実装の手順

STEP 1:スクリプトの差し替えと追加

新規スクリプトを作成:Sys_UIRoot.cs・Sys_InputManager.cs・UI_PanelBase.cs

既存ファイルを差し替え(上書き保存):Sys_Messenger.cs・UI_SaveLoadPanel.cs・Sys_DialogManager.cs

STEP 2:UI_Rootオブジェクトを作成する

これが今回の核心です。
散らばっていたUI系オブジェクトをここに集約します。

① Hierarchyの何もない場所で右クリック
  →「Create Empty」を選択
  → 名前を「UI_Root」に変更

② UI_Rootの位置をルート(最上位階層)に置く
  SaveManagerと同じ階層になるようにドラッグ

③ UI_RootにSys_UIRoot.csをアタッチ
  → これでDontDestroyOnLoadが有効になる

④ UI_RootにSys_InputManager.csをアタッチ
  → 入力ブロック管理の本体

既存のCanvas3つをUI_Rootの子に移動する

移動するもの(UI_Rootの子にする):
 Black_Canvas  ← 暗幕・FadeManager
 SaveLoad_Canvas ← セーブロード画面
 Dialog_Canvas  ← ダイアログ画面

移動しないもの(シーン内に残す):
 Backlog_Canvas ← シーンごとにリセットが必要なため
 System_set   ← ゲームデータ系のため

移動後のヒエラルキーイメージ:

ルート階層
 SaveManager     ← 既存・変更なし
 UI_Root       ← 今回新規作成
  ├ Black_Canvas   ← ここに移動
  ├ SaveLoad_Canvas ← ここに移動
  └ Dialog_Canvas  ← ここに移動

シーン内・変更なし
 Backlog_Canvas
 Dynamic_Canvas
 System_set
  └ ChapterManager ← 話し合い通りここに置く
 CUT_OBJECTs
 CAMERA_Main
 Cut_001〜

STEP 4:動作確認チェックリスト

□ Unityコンパイルエラーがない

□ Play開始時にUI_RootがDontDestroyOnLoadに移動している
  (再生中にHierarchyの「DontDestroyOnLoad」欄に
  UI_RootとSaveManagerが見えればOK)

□ セーブ画面を開く
  → パネルが表示される

□ セーブ画面を開いた状態でホイールダウン
  → ノベルが進まない ←今回の修正目的

□ セーブ画面を閉じてホイールダウン
  → ノベルが正常に進む

□ ダイアログが出ている間もホイールダウンで進まない

□ 右クリックでセーブ画面が閉じる

□ バックログを開いてホイールスクロール
  → ログがスクロールされる(従来通り)

□ バックログを閉じてホイールダウン
  → ノベルが進む(従来通り)

設定UI画面の管理システム構築 まとめ

共通できるものを管理し、入力操作での挙動を直し、UI画面への統一操作となりました。

現状では、ノベルパート1での実装となっており、このまま各設定をここで作ってしまって良いものかと迷っていました。
探索パート論争パートへの適用もどうなるか分かりません。
軽く聞いてみたところ、
各パート・変数に対しても、今回のセーブシステムは適用可能な感じだったので、安心しています。

早いうちに制御管理を構築できて良かったです(*´ω`*)
これで心置きなく「テキスト設定」「音量設定」にとりかかれます。

今回の目的

・セーブ画面表示中のホイールダウンでノベルが進行してしまう入力貫通問題の解消
・将来のUI画面追加を見越した共通管理システムの構築
・DontDestroyOnLoadの一元管理
・ヒエラルキーの整理

解決した問題

原因:Sys_Messenger.csのUpdate()にセーブ画面が開いているかのチェックが存在しなかった

解決:Sys_InputManager.csを新規作成し、UI画面が開いている間はIsBlocked = trueになる仕組みを導入。
Sys_Messenger.csのUpdate()冒頭にIsBlockedチェックを1行追加。

新規作成スクリプト

Sys_UIRoot.cs
・UI_RootオブジェクトをDontDestroyOnLoadに登録する
・このスクリプトをUI_Rootにアタッチするだけでよい
・UI系オブジェクトの親として機能する

Sys_InputManager.cs
・カウンター方式で入力ブロックを管理する
・Block() → カウンター+1
・Unblock() → カウンター-1
・IsBlocked → カウンターが1以上のときtrue
・複数UIが同時に開いていても正しく制御できる

UI_PanelBase.cs
・全UI画面の基底クラス
・Open() → InputManagerのBlock()を自動呼び出し
・Close() → InputManagerのUnblock()を自動呼び出し
・継承するだけで入力ブロック管理が自動化される

改修スクリプト

UI_SaveLoadPanel.cs
・UI_PanelBaseを継承
・Open(SaveLoadMode mode)でbase.Open()を呼ぶ
・Close()でbase.Close()を呼ぶ
・DontDestroyOnLoadの記述を削除(UI_Rootが管理)

Sys_DialogManager.cs
・UI_PanelBaseを継承
・Show()内でbase.Open()を呼ぶ
・OnYesClicked/OnNoClicked内でbase.Close()を呼ぶ
・DontDestroyOnLoadの記述を削除(UI_Rootが管理)

Sys_Messenger.cs
・Update()冒頭にIsBlockedチェックを追加
・IsBlocked == trueのとき全入力を無視する
・バックログのホイール操作には影響しない(IsBlockedチェックの後でbacklogActiveチェックがある)

Sys_FadeManager.cs
・DontDestroyOnLoadの記述を削除(Black_CanvasがUI_Rootの子になったため不要)

Sys_ChapterManager.cs
・DontDestroyOnLoadの記述を削除
・System_setの子としてシーン内に残す設計に変更
・章テキストはシーンごとに変わる情報のため、シーンをまたいで保持する必要がない

DontDestroyOnLoad管理ルール

呼んでよいのはルート直置きオブジェクトのスクリプトのみ
 Sys_UIRoot.cs  ← UI系を一括管理
 Sys_SaveManager.cs ← ゲームデータ系

禁止
 UI_Rootの子オブジェクトのスクリプト
  └ Sys_FadeManager / UI_SaveLoadPanel / Sys_DialogManager
 シーン内オブジェクトのスクリプト
  └ Sys_ChapterManager / Sys_BacklogManager

整理後のヒエラルキー構成

DontDestroyOnLoad対象・ルート
 UI_Root
  ├ Sys_UIRoot.cs   ← DontDestroyOnLoad管理
  ├ Sys_InputManager.cs ← 入力ブロック管理
  ├ Black_Canvas   ← Sys_FadeManager
  ├ SaveLoad_Canvas ← UI_SaveLoadPanel
  └ Dialog_Canvas  ← Sys_DialogManager
 SaveManager ← Sys_SaveManager・既存ルール通り

シーン内・DontDestroyOnLoad不要
 System_set
  ├ ChapterManager ← Sys_ChapterManager
  ├ CutManager
  └ ScreenshotManager
 Backlog_Canvas ← シーンごとにリセット
 Dynamic_Canvas
 TestSave_Canvas
 CUT_OBJECTs
 CAMERA_Main
 Cut_001〜

UI_Rootの配置ルール

・現在:ノベルScene1に配置(B案)
 → 現段階ではノベルScene1のみ開発中のため

・タイトルScene作成時:タイトルSceneにも追加(C案)
 → 重複はSys_UIRootのAwakeで自動削除される

・最終形:タイトルSceneのみに置く

・RuntimeInitializeOnLoadによる自動生成は採用しない
 → InspectorアサインによるUI構造を維持するため
 → Sys_Flag_Managerとは方針が異なる点に注意

将来UIを追加するときのルール

① UI_Rootの子にCanvasを新規作成する
② スクリプトはUI_PanelBaseを継承する
③ PanelObjectプロパティを実装する
  protected override GameObject PanelObject => panel;
④ 開くときはbase.Open()を呼ぶ
⑤ 閉じるときはbase.Close()を呼ぶ
⑥ DontDestroyOnLoadは書かない

→ 入力ブロックとDontDestroyOnLoadは自動管理される

現在までの設計ルール(継続・追記分含む)

・キャラクター・カメラはCutの外に置く
・Cutをまたいで使うオブジェクトはCutの外に置く
・SaveManagerはルートオブジェクト必須
・DontDestroyOnLoad対象はルートに置く
・演出のみのCutには必ずCmd_EndCutをアタッチ
・複数Cmd_Move_xyzがある場合
 EndCutを呼ぶのは最後の1つだけ
・DontDestroyOnLoadはルート直置きのスクリプトのみ記述
・UI_Rootの子・シーン内オブジェクトへの記述は禁止
・将来のUI画面は全てUI_PanelBaseを継承する

コメント