メニュー画面の構築【Unity6】

絶対調味ロジック

各UI画面へのアクセスとしての画面作りをしていきます。
ボタンはメイン画面に直置きも良いですが、汎用的な「メニュー画面」を実装していきます。
メイン画面に置くボタンは「メニュー」「スキップ」「オート」あたりになると思います。

メニュー画面の実装

Panel方式

今回はとくに機能的追加はないので、いままのでUI画面と同じ挙動の空画面を作るようなイメージです。
しかし、
そのまま各ボタンの配置(ボタンのほうに開く機能)」にするか「画面自体にボタン機能をつける」かで迷いました。
このあたりが若干、他のエンジンとの差を感じました。

空画面としての役割ならば、そのままボタン配置で良く、カスタム性もあります。
画面自体に機能を付けてボタンをアサインする方法でも、やってることは変わらないですが、
メニュー画面」の機能とすることで「メニュー画面」という位置付けにすることができそうです。

実際、将来は違う形になっているかもしれませんが、とりあえずこの形で実装してみることにしました。

いままで仮Canvasに置いてあったボタンたちをアサインして画面を作っていきます。
他のUI画面と同じPanel方式にもなったので、良かったのではないかと思います。

プロジェクトまとめ

目的

メイン画面から呼び出す「UIメニュー画面」の実装:各設定画面へのハブとして機能させる。

構成

UI_MenuPanel(UI_PanelBase継承
 ├ Closeボタン
 ├ Saveボタン
 ├ Loadボタン
 ├ System設定ボタン
 ├ Audio設定ボタン
 ├ Text設定ボタン
 └ Title戻るボタン

スクリプト構造:基本構造(他UIと統一)

  • UI_PanelBase 継承
  • Instance シングルトン
  • Awake() で初期化
  • Start() でイベント登録
  • Update() で右クリック閉じる

現在の仕様

  • UIは重ねて表示される
  • メニューの上に各設定画面が出る
  • 各UIは独立した「UI_PanelBase」管理

UIスタック管理システム(入力専用)

現状、メニュー画面他UI画面連動しているので、閉じる挙動ですべてが閉じる形になっています。
なので、画面を別々に制御するために「UI_Manager」を作って管理させます。

全体構成

UI_Manager(新規)
 ├ Stack
 ├ 入力処理(右クリック)
 └ CloseTop()

UI_PanelBase(修正)
 ├ Open → Register
 └ Close → Unregister

各UI
 └ Update削除(←重要)

将来の分離

  • UI_Manager → UI制御
  • Sys_InputManager → ゲーム入力制御

配置ルール

  • UI_Manager → UI_Root外(管理系)
  • UI → UI_Root内(表示系)

UIの開き方を2パターンから選べるようにする

メニュー画面の将来性を考えてみると、

  • フル画面のメニュー画面が開かれる → 他UI画面が重なっていく
    透明度にもよるが、メニュー画面自体は表示されたままでも良い
  • フル画面ではなく、上下左右からボタン尺の飛び出すメニューを作る場合、
    他UI画面を表示する際にその表示されたメニューは閉じてほしい

という挙動を想定できます。
もしくは、UI画面がさらに重なった場合、下のUI画面はそのままでいい場合と閉じたい場合。

この2つの挙動を選択設定できるように機能を追加します。

UI画面Canvas)毎に設定が選べるようになったので、臨機応変に挙動を変えられます。

今回のメニュー画面はボタン配置だけだったので、すんなり実装と想定していたのですが、
他UI画面と同じ挙動(開く閉じる)に統一するのにちょっと手間取りました。
いままでスクリプトを作り上げてきたのは自分ではないので、スクリプトの構成を理解できていないのが大きな要因です。

あとはChatGPTの途中提案に「モーダル制御」(画面操作の誤作動をより制御するシステム)というものがあり、
どうせならばと試みたのですが、どうにもごちゃごちゃしてきてしまい、
元に戻す工程でUnityプロジェクトにエラー頻発で結構焦りました…

そして、
とりあえず当初の目標であるUI画面がすべてそろったので、
保留だった自動でソート(画面の重なり具合)を決めてもらう「UIソートシステム」も実装しておきました。

内部で何が起こってるのかよく分かっていませんが、きっとうまく動いてるはずです…

UI自動ソートシステム

目的

  • UI画面の表示順序を自動で管理し、手動での SortOrder 管理を不要にする
  • Stack 管理により、Additive / Replace モードに対応
  • 右クリックでトップUIを閉じる挙動を統一
  • UI_Root 配下のCanvasのみ自動ソート対象とすることで、暗幕や外部Canvasの影響を排除

構成

UI_PanelBase.cs(全UI共通基底)

  • OpenMode 列挙型
    • Additive:重ねて表示
    • Replace:開くとき全UIを閉じる
  • Open()
    • Replace の場合は UI_Manager の CloseAll() 呼び出し
    • 入力ブロック(Sys_InputManager)
    • PanelObject のアクティブ化
    • Canvas の SortOrder 自動設定(UI_Root 配下のみ、Black_Canvas は固定999)
    • UI_Manager へ登録
  • Close()
    • 入力ブロック解除
    • PanelObject 非アクティブ化
    • UI_Manager から登録解除

UI_Manager.cs(UI全体管理)

  • Stack による UI 登録・削除:Register()/Unregister() で Stack 管理
  • GetNextSortOrder() で次のSortOrderを取得
  • CloseTop() で Stack の最上位 UI を閉じる(右クリック用)
  • CloseAll() で全UIを閉じる(Replace モード用)
  • Update 内で右クリック監視 → CloseTop() 呼び出し

自動SortOrderのルール

対象挙動
UI_Root 配下の CanvasOpen 時に Stack 順で 1,2,3… と自動設定
Black_Canvas(ロード時の暗幕)固定 999(最前面)
UI_Root 外 Canvas自動設定なし、必要に応じ手動

※ 初期値はすべて 0 に揃えると安全:さらに安全策として以下の設定を行い現状をキープ

ダイアログ200
UI_Root 配下の Canvas100
UI_Root 外 Canvas0

利用ルール

  1. UI_Root 配下に Canvas を作成
  2. UI_PanelBase を継承
  3. PanelObject をアサイン
  4. base.Open()/base.Close() を呼ぶ
  5. SortOrder 初期値は 0 で OK
  6. Replace モードを使う場合は openMode = OpenMode.Replace を設定

効果

  • UI表示順序を自動管理 → 手動での SortOrder 設定不要
  • Stack 管理により右クリック閉じや Replace での挙動が安定
  • UI_Root 配下の Canvas は常に表示順が保証される
  • 外部Canvasや暗幕は影響を受けない

・将来的に新しい UI を追加するときも、この基盤を使うだけで自動で最前面に表示される
・Replace / Additive の挙動をインスペクターで選択可能

追加修正

  • UIをすべて閉じたあとに SortOrder が増え続ける問題
  • 次回オープン時に 1,2,3 に戻らない問題

修正方針スタックが空になった瞬間にリセット

修正箇所①(最重要):Unregister に追加

if (stack.Count == 0)
{
    nextSortOrder = 0;
}

UIが閉じられる経路は複数ある:唯一共通するのが Unregister

  • CloseTop
  • 個別Close
  • Replace(CloseAll経由)
  • 手動Close

修正箇所②(保険):CloseAll の最後

nextSortOrder = 0;
  • 念のための二重保険
  • 将来仕様変更しても壊れにくい

CloseAllの注意点(ついでに修正済み)

while (stack.Count > 0)
{
    var top = stack.Peek();
    top.Close();
}
  • Close() → Unregister() が内部でPopするため
  • 二重Pop防止

最終挙動

  • 1 → 2 → 3 → 全閉じ → リセット → 1 → 2 → 3
  • 常にクリーンな状態から再スタート

コメント