メイン画面内UIの実装【Unity6】

絶対調味ロジック

メイン画面内のUIを作成していきます。
カメラ枠UIと同様に、こちらにもいくつかシステムと連動させたいUIがあります。

  • 個別イコライザー:BGM・SE・Voiceに対応したイコライザーを3本
  • ●REC表示:表示を未読既読に対応
  • FPS表示:実測定値の表示
  • 音量数値:マスター音量の数値部分
  • ウィンドウサイズ:各ウィンドウサイズ時とフルスクリーン時に対応した画像
  • オートモード表示:通常進行とオートモード時の変化

これらが連動できれば、とりあえず想定デザインが完了します。

個別イコライザー

2種類のデザイン

共通の仕組み(オーディオ解析)

  • 解析手法:AudioSource.GetOutputData() を使用したRMS(二乗平均平方根)算出。
  • PlayOneShotでの再生は波形データが取得できないため、ボイス等は Play() 方式に統一。
  • 感度(Sensitivity)
    取得した数値は微小なため、係数を掛けて UI に適した 0.1〜1.0 程度の範囲に調整する。

【パターンA】画像を伸ばすバージョン (Fill)

画像を歪ませたり、タイリングしたりせずに「割合」で見せたい場合に最適。

UI設定
・Image Type: Filled
・Fill Method: Vertical (または Horizontal)
・Fill Origin: Bottom

スクリプトの役割:計算した音量(0〜1)をそのまま image.fillAmount に代入します。

メリット:最も設定が簡単で、グラデーションのある画像なども綺麗に表示できます。

【パターンB】タイル(ブロック)バージョン

ドット絵や決まった形のブロックを積み上げるデザインです。

UI設定
Sprite Import:Mesh Type を Full Rect に変更(タイル間の隙間防止)
Image:Image Type を Tiled に設定
RectTransform:Pivot を (0.5, 0)(下端中央)に設定。※高さ変化を上方向に限定するため

スクリプトの役割:音量に合わせて rectTransform.sizeDelta の Height (y) を操作します。

1ブロック単位の制御
Mathf.Round(現在値/1ブロックの高さ)*1ブロックの高さで数値を丸めることで、中途半端なブロック表示をカット

メリット:アーケードゲームやオーディオ機器のような「パキパキ感」が出ます。

新規スクリプト「Sys_AudioVisualizerTild」

高さ上限」「感度」「速さ」のパラメータを装備しています。
なかでも「感度」が重要で、1~2くらいにしないと反応が得られませんでした。

対象別:接続プロトコル

SE・Voiceともに、ロード後にオーディオソースが「Missing」する問題が発生。
タイプを指定して専用コードを実行するように改善しました。

カテゴリ接続モード仕組み・工夫
BGMSingleSource特定の AudioSource を直接監視。
SESEManagerSys_AudioManager の全プール(10個)を毎フレーム走査し、合計音量を算出。
Missing問題を解消。
VoiceSingleSourcesharedVoiceSource を監視。
Missing自動復帰機能により、ロード後も自動で再接続。

各スクリプトへの変更点(重要)

Sys_AudioVisualizerTiled(新規/メイン)
Missing自動接続機能:ロード時に参照が切れても、Sys_AudioSettingsManager.Instance を経由してボイス用ソースを自動で再取得するように設計。
感度(Sensitivity)調整:インスペクターから 0.1〜150 の範囲でリアルタイム調整可能。

Sys_AudioManager(SE管理)
新機能 GetTotalLevel():10個ある再生中の全ソースから音量をかき集めて平均化する機能を追加。

Sys_AudioSettingsManager(設定管理)
再生方式の変更:PlayVoiceOrBlip 内で PlayOneShot を廃止し、clip の代入による Play() 方式へ変更。
これにより波形データの取得が可能に。
窓口機能 GetSharedVoiceSource():イコライザーからの「ソースを教えてほしい」というリクエストに応答する機能を追加。

運用上の注意(トラブルシューティング)

  • Pivot設定:メーター画像の Pivot の Y は必ず 0 にしてください(下から上に伸びるため)。
  • Sprite設定:タイル画像(MSC_Sound_line)のインポート設定で Mesh Type を Full Rect にすることで、
    重なりや隙間を防げます。
  • 参照エラー
    UnassignedReferenceException が出たら、
    インスペクターで AudioSource や RectTransform の枠が「None」になっていないか確認。
  • 音量設定との切り分け
    AudioSource のボリュームを 0 にすると、GetOutputData も 0 になる。
    ミキサー経由で音を消しても「動かしたい」場合は、AudioMixer から直接データを取る特殊な手法が必要。

追加実装:音量設定連動システム

1. 実装の核:二重チェック・ロジック

イコライザーの Update 処理の最優先項目として、以下の「ゲート」を追加しました。
第1ゲート(Master): マスターボリュームが 0 または Mute の場合、全メーターが即座に停止。
第2ゲート(Individual): 各メーターに設定された ChannelType(BGM/SE/Voice)が 0 または Mute の場合、そのメーターのみが停止。
これにより、「音が出ていないのにメーターだけ動いている」という不自然な挙動を完全に排除しました。

2. インスペクターの変更点

イコライザー側に新しい設定項目を追加しました。

Channel Type (Enum)
そのメーターがどの設定グループに属するかを選択します(BGM / SE / Voice)。
これを設定することで、Sys_AudioSettingsManager 内の該当する音量スライダーやミュートボタンの状態を自動的に監視しに行きます。

3. 設定ガイド

メーター対象Channel Type の設定期待される挙動
BGM用BGMBGM音量0 または BGMミュート または マスターミュートで停止
SE用SESE音量0 または SEミュート または マスターミュートで停止
ボイス用Voiceボイス音量0 または ボイスミュート または マスターミュートで停止

4. 既存システムへの影響と安全性

非破壊的修正:Sys_AudioSettingsManager の既存の値を「参照(Read)」しているだけなので、
音量設定の保存・読み込みロジック自体には一切影響を与えません。
即時反応: Update 内で毎フレーム判定を行っているため、プレイヤーが設定画面でスライダーを動かした瞬間に、メーターの動きがリアルタイムに同期します。
低負荷:判定は単純な if 文による数値比較のみであるため、計算負荷の増加はほぼゼロです。

●REC表示

UIアニメーションスクリプトの使いまわしで、2コマアニメです。
通常SpriteUIスプライトでスクリプトを分けないといけないところが注意点です。

既読状態によるGameObject置換

テキストの「未読」「既読」に対応させて「録画状態」と「再生状態」のイメージ置換を行います。

1. ヒエラルキー構造(推奨)

スクリプト自体の停止(自己消滅)を防ぐため、「制御用の親」と「表示用の実体」を分けて管理します。
ReadStateController (常にActiveな親 / Sys_ReadStateObjectSync をアタッチ)
REC_Img (未読用 / unreadObject にアサイン)
PLAY_Img (既読用 / readObject にアサイン)

2. 制御ロジック(Sys_ReadStateObjectSync.cs)

イベント監視(Update)を廃止:毎フレームの負荷と判定のズレを回避。
受動的なリフレッシュ:メッセンジャーからの直接命令(ForceRefresh)があった時のみ、Sys_SkipManager を参照して SetActive を切り替える構成にしました。

3. メッセンジャーとの連携(Sys_Messenger.cs)

以下の2つのタイミングで、イメージの更新を外部(Syncスクリプト)へ通知するようにしました。
表示開始時:ShowNextMessage メソッド内で実行。新しいメッセージが出た瞬間に判定を反映。
表示完了時: DisplayMessage コルーチン内の既読フラグ(MarkAsRead)が立った直後に実行。
文字が流れ終わった瞬間に「未読→既読」へのアニメーション切り替えを発生させます。

4. 注意点

コルーチンの重複:ShowNextMessage 内で StartCoroutine が2回呼ばれないよう整理済みです。
判定の同期:テキストの色(messageText.color)を変えるロジックと、イメージを切り替えるロジックの参照元を統一したため、表示の矛盾が発生しなくなっています。

FPS表示

1. 実装の核心(低負荷へのこだわり)

ゼロ・アロケーション (Zero GC Alloc)
Update 内での ToString() や文字列結合を完全に排除。
Awake 時に 0.0 〜 100.0 までの全パターン(1001個)を string[] にキャッシュし、実行時はインデックス参照のみで表示を更新します。
非依存性の確保:
Time.unscaledDeltaTime を使用しているため、ゲーム内の Time.timeScale(スローモーション演出等)の影響を受けず、常に正確な実測値を計測します。
リソース消費の最小化:
数値のみに特化し、余計な計算や判定ロジックをすべて削ぎ落とした「演出用実測カウンター」として構成。

2. スクリプト仕様

項目内容
ファイル名Sys_FPSCounter.cs
依存コンポーネントTextMeshProUGUI
表示範囲0.0 〜 100.0 (100以上は100.0固定)
精度小数点第1位
更新頻度updateInterval による任意設定(推奨: 0.5s)

3. セットアップの注意点
TextMeshProの設定
数字が切り替わる際のガタつき(文字幅の違いによる揺れ)を防ぐため、Inspectorから Alignment を「Right(右揃え)」 に設定してください。
また、フォント設定で Monospaced(等幅) を有効にするとより安定します。
描画負荷の軽減:
このHUD専用に数字(0-9)とドット(.)などのみを含めたフォントアセットを作成し、テクスチャサイズを最小化してください。

4. 将来的な拡張案(ランダム更新)
将来的な拡張:更新タイミングのランダム化
現在は一定間隔(例:0.5秒ごと)で更新していますが、これに「揺らぎ」を加えることで、より「生きた計器」としての演出効果が高まります。
実装案
updateInterval を固定値ではなく、更新完了時に Random.Range(0.4f, 0.6f) などの範囲で次の更新時間を再計算するロジックに変更。
メリット:
一定周期の機械的なパタパタ感を排除し、システムがリアルタイムに高負荷な計算を処理しているような「切迫感」を演出できます。

音量数値

外枠時にマスター音量と連動させるために作った「UI_QuickSettings」を使いまわします。
「数値」の部分だけをアクティブにしておきます。

ウィンドウサイズ

UI_QuickSettings」に機能を追加します。
ウィンドウサイズだけでなく、フルスクリーン時にも対応させました。
解像度・フルスクリーン連動UIシステム(堅牢版)です。

1. 概要

Sys_SystemSettingsManager の状態(解像度・フルスクリーン)を監視し、特定の Image コンポーネントの Sprite を動的に差し替える機能です。
設定ミス(Null)によるクラッシュを防ぐガードロジックを全域に展開しています

2. 技術的ポイント

マルチステート監視
ResolutionPreset(解像度)だけでなく、bool(フルスクリーン状態)のどちらかが変化した際にも反応するように拡張されています。
Null-Safe 設計(堅牢性)
各 UI コンポーネント(Slider, Text, Image)および画像リソース(Sprite)への参照を、使用直前に必ず null チェックしています。
これにより、一部の設定が漏れていても、他の正常な機能(音量調整など)を止めずに実行し続けます。
効率的なイベント管理
Update 周期での監視は、値の不一致(差分)がある場合のみ処理を走らせる「差分更新」方式を採用しており、CPU負荷を最小限に抑えています。

3. 動作の優先順位

現在のロジックでは 「フルスクリーン画像」が最も優先 されます。
フルスクリーンを解除すると、選択中の解像度画像に切り替わります。

オートモード表示

新規スクリプト「Sys_AutoModeImageSync
このスクリプトは非常に軽量で依存関係も少ないため、ボタン以外の装飾的なUI(インジケーターや背景の一部など)にもそのまま使い回すことが可能。

1. 概要

Sys_TextSettingsManager の AutoMode(bool値)を監視し、特定の Image コンポーネントの Sprite を動的に差し替える機能です。
「スキップ中はオートが強制解除され、かつオブジェクト自体が非表示になる」という既存システムとの親和性を重視し、再表示(スキップ解除)時に正しい画像で復帰するように設計されています。

2. 技術的ポイント

ステート監視(Polling Strategy)
Update 内で bool 値の比較のみを行い、変更があった瞬間だけ targetImage.sprite の書き換えを実行します。
計算負荷が極めて低く、実行時のパフォーマンスに影響を与えません。
初期化・復帰の保証
Start および OnEnable(追加推奨)にて現在の状態を即座に反映します。
これにより、スキップ演出による「非アクティブ → アクティブ」の切り替えが発生しても、表示の矛盾が発生しません。
単機能設計(Single Responsibility)
「表示・非表示の制御(スキップ連動)」と「中身の入れ替え(オート連動)」をあえて別コンポーネントに分けることで、バグの切り分けが容易な、Unityのスタンダードな設計思想に基づいています。

3. 仕様の整合性

スキップ解除時の挙動
「スキップ解除 = オートOFF = オート連動オブジェクトが再表示」という流れになります。
このとき、本スクリプトによりスプライトは確実に Normal Sprite に戻った状態で画面に現れます。
拡張への備え
将来的に「既読スキップが終わったら自動的にオートに戻したい」という仕様変更があった場合でも、このスクリプトは AutoMode のフラグさえ見ていれば自動で追従するため、コードの修正は不要です。

実機での挙動

実装を警戒していた「イコライザー」はつまずくことなく実装できました。

ブロック型挙動のつもりで、最初は伸縮型になってしまったところはむしろ想定内。
伸ばさず積み上げていくタイプは大変かなとも思いましたが、タイル型にすればよいだけだったので簡単でした。

ビジュアライザーとして「」の線が伸縮するやつなどは本格的実装が必要そうでした。
タイトル画面でBGMと連動させる何かは作りたいとおもっているので、その時に新チャレンジになると思います。

セーブロード時の挙動で、効果音実装時に起こった「Missing」問題が発生しました。
新実装すると、やはりセーブ関連でなにかしら引っ掛かりますね。
事前に起こっていた問題だったので、時間をかけずに原因が分かったのも良かったです。

FPS表示」に関しては、ただのカッコつけ演出です。
実際の計測結果を出しつつ軽量スクリプト、というこだわりをGeminiに頼みました。

そもそもスクリプトで「60FPS」に抑え込んでいるので、それ前後しか振れ幅はありません。
限界値などで色を変える演出も、という提案も魅力的でしたが、今回は我慢です。

バッテリー」は、各パートで20%ずつ減っていくだけなので、特に演出は考えていません。
切れそうになってからの赤く点滅もいつかはやってみたいですね。

UIの外側と内側がこれで完成しました。
ここまでの実装で不具合も結構たまってきているので、それらの対応をしつつ、
やっとこさ「探索パート」もしくは「他ノベルパート」に着手できるかな、といった感じです。

各パートはコピーで増やすので、なるだけ不具合修正・演出追加は終わらせておきたいところなのですが、
それしてると本当に進まないような気もしますし、どうすべか…(´・ω・`)

■追記
未読既読でのオブジェクト置換を追加実装しました。
判定取得で手間取りましたが、なんとかアニメーションするイメージ同士を置換することができました。

コメント