テキスト設定の構築★【Unity6】

絶対調味ロジック

セーブシステムができあがっているので、
今度こそ既読判定を含めた「テキスト設定」の実装に挑戦できます。

自分が思っているスタンダードと他エンジン・既存ノベルゲームの仕様を比べて、設計仕様を考えていきます。

テキスト設定の設計案

【UI_TextSettingsPanel】
継承:UI_PanelBase
配置:TextSettings_Canvas(UI_Root配下)

項目一覧

  • スキップモード
    2択排他ボタン:[すべて] [既読のみ]
    デフォルト:既読のみ
    SYS_SKIP_MODE(int: 0=既読のみ 1=すべて)
  • オートモード
    2択排他ボタン:[ON] [OFF]
    デフォルト:OFF
    SYS_AUTO_MODE(bool)
    ※実際のオート進行はMessengerへの追加実装が別途必要
  • テキストスピード
    スライダー:1〜100(整数)
    デフォルト:80(≒現行0.1f相当)
    マッピング:Lerp(0.5f, 0f, (val-1)/99f)
    SYS_TEXT_SPEED(int)
  • オートウェイト
    スライダー:1〜10(整数)
    表示:「○秒」形式
    デフォルト:3
    SYS_AUTO_WAIT(int)
  • メッセージウィンドウ透明度
    スライダー:0〜100(整数)
    デフォルト:100
    SYS_WINDOW_ALPHA(int)
    適用先:プレビューウィンドウ・メイン画面Msg_Panel

プレビューエリア

・画面下部に模擬メッセージウィンドウ
・テキストスピード変更時にループ文字送りで即時反映
・透明度変更時にリアルタイム反映
・テスト用固定セリフ表示

保存ルール

・全項目SYS_フラグ(global_system.json)
・変更即時保存
・ロード・セーブデータ切替に影響されない

入力ブロック

・Open()でInputManager.Block()自動呼び出し
・Close()でInputManager.Unblock()自動呼び出し
・右クリックで閉じる
・閉じるボタンで閉じる

将来対応メモ

・オートモードの実際の進行処理はMessenger追加実装
・演出速度変更はCmd_TextSpeed.csで対応:実装時はMessengerにSetTextSpeed(float)を追加するだけ
・既読フラグリセットボタンは既読システム完成後に追加

Hierarchy構築

【DontDestroyOnLoad・ルート直下】
 UI_Root
  ├ Sys_UIRoot.cs
  ├ Sys_InputManager.cs
  ├ Black_Canvas
  │ └ Black_Screen
  ├ SaveLoad_Canvas ← UI_SaveLoadPanel.cs
  │ └ Panel(初期Inactive)
  ├ Dialog_Canvas ← Sys_DialogManager.cs
  │ └ Panel(初期Inactive)
  └ TextSettings_Canvas ← UI_TextSettingsPanel.cs ← 新規Canvas追加
    └ Panel(初期Inactive)
     ├ Title_Text
     ├ Close_Btn ← Button(base.Close()を呼ぶ)
     ├ SkipMode_Section
     │ ├ SkipMode_Title
     │ ├ SkipAll_Btn ← Button
     │ └ SkipRead_Btn ← Button
     ├ AutoMode_Section
     │ ├ AutoMode_Title
     │ ├ AutoOn_Btn ← Button
     │ └ AutoOff_Btn ← Button
     ├ TextSpeed_Section
     │ ├ TextSpeed_Title
     │ ├ TextSpeed_Slider ← Slider
     │ └ TextSpeed_Text ← TextMeshProUGUI(数値表示)
     ├ AutoWait_Section
     │ ├ AutoWait_Title
     │ ├ AutoWait_Slider ← Slider
     │ └ AutoWait_Text ← TextMeshProUGUI(「3秒」表示)
     ├ WindowAlpha_Section
     │ ├ WindowAlpha_Title
     │ ├ WindowAlpha_Slider
     │ └ WindowAlpha_Text ← TextMeshProUGUI(数値表示)
     └ Preview_Section ← 画面下部
      ├ PreviewWindow(Image)← Image(背景)+ メッセージ模擬表示
      └ PreviewMessage ← TextMeshProUGUI

SaveManager ← Sys_SaveManager.cs
 TextSettingsManager ← Sys_TextSettingsManager.cs
 Sys_Flag_Manager(自動生成)

【シーン内・DontDestroyOnLoad不要】
 System_set
  ├ ChapterManager ← Sys_ChapterManager.cs
  ├ CutManager ← Sys_CutManager.cs
  └ ScreenshotManager ← Sys_ScreenshotManager.cs
 Dynamic_Canvas
  └ Msg_UI
   └ Msg
   ├ Name_Plate
   ├ Msg_Panel ← 透明度適用対象
   ├ Msg_Panel_Wait_Icon
   └ Msg_Btn
 CAMERA_Main
 CUT_OBJECTs
 Cut_001〜(初期Inactive)

テキストスピード・ウィンドウ透過・プレビュー

3つの機能をまず実装

新規スクリプトで「Sys_TextSettingsManager」を作成します。
キー定数やデフォルト値、絶対変数で設定が調整されているかどうかのチェックなどの挙動マネージャーです。

新規スクリプトで「Sys_TextSettingsPanel」も作成します。
UI画面用スクリプトです。

問題① テキストスピードがプレビューに反映されない。
問題② ウィンドウ透明度がメイン画面に反映されない。
問題③ スキップモード自体はまだ機能しない。
問題④ オートモード自体はまだ機能しない。
問題➄ オートウェイトもまだ確認できず。

機能自体はすんなりと実装が済みました。
「プレビュー」にリアルタイム反映が、少し調整が必要でした。

実機での挙動

スキップ・既読判定・既読クリア

スキップ実装にともなう懸念点

■ セリフキー大量問題について

想定されるメッセージ総数:
・1シナリオ 約1000〜3000メッセージ(中規模ノベル)
・キー例:SYS_READ_a01_Cut001_0 など
global_system.jsonへの影響(絶対変数の保存データ):
・1キーあたり約30〜50バイト
・3000キーで約150KB
→ ファイルサイズとしては問題なし

メモリへの影響:Dictionaryに3000エントリ
→ これも問題なし・誤差レベル

テクスチャ・ドローコールへの影響:
→ 完全にゼロ
→ これはファイルI/OとメモリのみでGPUは無関係

■ 既読文字色変更について

実装方法:
・Messengerのメッセージ表示時にSYS_READ_xxx を確認
・既読なら TextMeshProUGUI の colorプロパティを変更
・未読なら通常色で表示

懸念点:
・文字送り中に色を変えるのか、表示完了後に変えるのか
→ 表示完了後に変える方がシンプルかつ視覚的にも自然

・色はどこで定義するか
→ MessengerのフィールドにreadColor / unreadColor を追加

■ 早送りスキップの負荷について

高速文字送りの負荷:
・WaitForSecondsが極小になるだけ
・1フレームに複数文字処理が走る
・テキストメッシュの更新が高頻度になる

実際の影響:
・FPSへの影響はほぼなし
・ただしUpdatePanelSize()が毎文字呼ばれている
→ ForceMeshUpdate()が高頻度で走る
→ スキップ中はUpdatePanelSize()をスキップする最適化が有効

■ 代表的なノベルゲームのスキップ挙て

吉里吉里系(クラナド・Fate等):
→ 文字送りを最速にしてAutoで自動進行
→ セリフは一瞬表示されて次へ
→ 演出は通常速度で流れる(待つ)
→ または演出をスキップして次Cutへ

NScripter系:
→ Ctrlキーで高速送り
→ 文字は即表示・ウェイトは短縮
→ 演出は一部スキップ

■ 速度イメージ

1メッセージあたり:ほぼ0秒(即表示)
1Cutあたり:0.1〜0.3秒程度(メッセージ数×ほぼ0秒+画面更新)
演出Cutがある場合:DOTweenの時間だけ待つ(例:0.5秒の移動演出なら0.5秒止まる)

スキップの設計案

  • 新規作成:
    Sys_SkipManager.cs
    → スキップ状態の管理(IsSkipping)
    → スキップ開始・終了の制御
    UI_SkipButton.cs
    → スキップボタンの制御
  • 改修:
    Sys_Messenger.cs
    → IsSkipping中は文字即表示+自動進行
    → 既読フラグの記録
    → 既読色の適用
  • SYS_フラグ:
    既読フラグ:SYS_READ_{sceneName}{cutIndex}{messageIndex}
    → Bool型
    → 既読時にtrue
  • 設定連動:
    SYS_SKIP_MODE(0=既読のみ・1=すべて)を参照
    → 既読のみの場合:未読メッセージで自動停止
    → すべての場合:既読未読関係なく進む

Hierarchy構造

【ルート直下・DontDestroyOnLoad】
 UI_Root
  ├ SaveManager
  ├ TextSettingsManager
  ├ SkipManager ← 新規追加・Sys_SkipManager.csをアタッチ
  └ Sys_Flag_Manager(自動生成)

【シーン内】
 TestButtom_Canvas
  ├ SaveButton
  ├ LoadButton
  ├ TextButton
  └ SkipButton ← UI_SkipButton.csをアタッチ

スキップ・既読判定・既読クリアの実装

まずは「スキップボタン」の設置です。

「SkipManager」を設置します。
セーブ・テキストのマネージャーと同じく、ルート直下に置くようにします。
マネージャーも増えてきているので、まとめたいですが「ルート直下ルール」があるため、どうしようもなさそうです。

「既読色」は、キャラクター毎に割り振っている「Messenger」内で設定します。
これによりキャラクター毎に色を変更できるようにもなっています。
(実際には統一しておかないと余計な混乱を招きそうですが)

しかしここからが、けっこうな試練でした。
既読・未読の判定機能自体は、リアルタイムプレイ・セーブロード後プレイでも問題なし。
問題はロード後の「既読色」のタイミングがうまくいかず。
既読状態でセーブロードすると、未読部分にも既読色がついてしまいます。

テスト → 失敗 → 原因追及・修正 → テスト → 失敗 → 原因追及・修正 → の6時間ループ。
Claudeさんのセッション制限も90%超える事態に…

いろいろ考えて試してくれていましたが、
原因は未読テキスト色の2つの同時設定と、どちらを指定して基準にするか、というところだったみたいです。

未読色の設定:
1. UIでのTMP設定
2. 未読指定スクリプトでの決定

TMP色を取得しようとすると、うまくタイミングが合わないようでした。
なので「既読色」設定と同じように「Messenger」内で設定する、ということになりました。

結果的に、キャラクター毎に「未読色」も設定できるようになって応用演出ができるようになりました(*´ω`*)

ここまできて会話システムの「Sys_Messenger.cs」に多重管理問題がのしかかります。
実装当時よりもMessengerが多くのことをやりすぎています。

「動いているコードは触るな」に納得してしまいましたw

なんとかスキップと既読色問題も解決したので、
提案されていた「既読クリア」ボタンも追加してみることに。このボタンは考えてもいなかったものです。
プレイヤー自身に既読をクリアする方法があるのは良いと思いました。
よく考えたら「設定」の「初期設定に戻す」ボタンも同じようなものですね。

そして「初期設定に戻す」ボタンも付けたほうが良いと気がつくのであった…

「UI_TextSettingsPanel.cs」に「既読クリア」機能を追加してもらいました。

実機での挙動

Cinemachineカメラ移動スキップ★0729

Cinemachine導入に伴い、シネカメラの移動時のスキップができそうだったので、実装しました。

■ 概要・課題

会話やカット進行のスキップ中(Sys_SkipManager 稼働時)、Cinemachineのカメラ移動(ブレンド時間)が通常の速度のままだと演出とテキスト進行のテンポが噛み合わない課題があった。
既存コード(Sys_Messenger, Sys_CutManager)を変更せず、かつシーン内の多数のVCamを個別に設定する手間を避けた、堅牢でメンテナンス性の高い一元管理システムを導入した。

■ アーキテクチャと採用理由

  • 一元管理方式の採用 (CinemachineBrain 制御)
    各VCamではなく、シーン全体のブレンドを統括している CinemachineBrain.DefaultBlend をスキップ状態に応じて上書き変更する方式を採用。
  • ブレンド時間制御(0.1秒 前後設定)
    移動時間を完全な 0秒 (Cut) にすると超高速テキスト送り時にコマ飛びや目への負担が生じるため、インスペクターから調整可能な高速ブレンド(0.1秒 等)を設定できるようにした。

■ 導入手順・運用ルール

  1. 配置手順
    シーン内の Main Camera(CinemachineBrain が付いているオブジェクト)、またはシステム管理用オブジェクトに Sys_CinemachineSkipAdapter(新規スクリプト) を 1つアタッチ する。
  2. VCam側の設定
    Cut 内に配置している個々の VCam へのアタッチや参照登録は 一切不要。
  3. パラメータ調整
    インスペクター上の skipBlendTime(移動時間)と skipBlendStyle(補間タイプ)で、演出の心地よさに応じて調整を行う。

Cinemachineカメラ移動スキップ改善★0909

スキップ実行時に Virtual Camera(VCam)間の移動・遷移が遅延する問題を解消し、動作の高速化と安全性を両立させました。

■ 変更の目的と概要

目的:
スキップモード中、Cinemachine によるカメラ遷移(ブレンド)を強制的に高速化(または即時適用)させる。

解決した課題:
・Custom Blends の優先問題:DefaultBlend の短縮だけでは、個別に割り当てられた Custom Blends(特定の VCam 間の長時間の遷移設定)が優先されてスキップが効かない状態になっていた点を解消。
・型依存と実行環境エラーの回避:Unity 6 (Unity.Cinemachine 3.x) の内部型変更や、IL2CPP/ビルド環境による dynamic キーワードのエラー(CS0656 等)を回避し、リフレクションを用いた軽量かつ保守性の高い実装へ変更。
・初期化順序の接続漏れ:OnEnable のタイミング依存を防ぎ、Start でもイベント登録と状態の同期を行うよう強化。

■ 変更されたコンポーネント

Sys_CinemachineSkipAdapter.cs
・スキップ開始時(isSkipping = true):DefaultBlend を指定の短時間(デフォルト 0.1 秒)に変更し、CustomBlends を一時的に null(退避)に設定。
・スキップ終了時(isSkipping = false):DefaultBlend および CustomBlends を元の設定に安全に復元。
・プロパティ操作を文字列によるリフレクション処理に変更することで、パッケージの型依存を完全排除(常時負荷ゼロ)。

■ 運用・構築ルール(今後の注意点)

配置方法:
CinemachineBrain がアタッチされているカメラオブジェクト(または管理オブジェクト)に Sys_CinemachineSkipAdapter をアタッチして運用。
ブレンド時間調整:
スキップ時の遷移速度を調整したい場合は、インスペクターの skipBlendTime(推奨: 0.05〜0.2 秒)で変更可能。

ロード時におけるカメラブレンド発生★0730

■ 概要・発生していた問題

現象:
セーブデータをロードした際、本来ならロード先のカメラ位置へ瞬時に切り替わるべきところ、以前のカメラ位置から移動するブレンド運動(スライド移動)が画面上に表示されてしまう。

原因:
ノベルパートのロード復元処理(Sys_SaveManager 内のカット遡り再生ループ等)において、複数VCamの優先度(Priority)やアクティブ状態が短時間で大量に切り替わるため、CinemachineBrain がそれを通常の演出用カメラ移動と判定し、設定されたブレンド時間(DefaultBlend)を使って補間計算を開始していたため。

■ 変更内容と仕組み

暗転(フェードアウト)している裏側でカメラ位置を確定させ、
フェードイン前に設定を元に戻す「1フレーム即時カット(Cut)挟み込み方式」を採用・実装しました。

① Sys_CinemachineSkipAdapter.cs の機能拡張
ロード時など、一時的にカメラの遷移時間を0秒に強制固定するための制御メソッドを追加。
・SetInstantCut():CinemachineBrain.DefaultBlend を一時的に Style = Cut / 0秒 に変更。
・RestoreDefaultBlend():現在のスキップ状態(IsSkipping)に合わせて、通常時のブレンド設定またはスキップ時の短縮ブレンド設定へ安全に復帰させる。

② Sys_SaveManager.cs のロードコルーチン(LoadRoutine)の修正
全データの復元完了後・画面フェードイン(FadeIn())直前のタイミングで以下を割り込み処理。
・1. 暗転中に SetInstantCut() を実行
・2. yield return null;(1フレーム待機)
→ CinemachineBrain に最終決定した VCam の位置へ即時ジャンプ(補間なし)させる。
・3. RestoreDefaultBlend() で設定を本来の状態に復元
・4. 画面フェードイン開始

■ 本実装のメリット(堅牢性・設計上の強み)

  1. 視覚的完全性の保証: 暗転中に位置確定が完了するため、プレイヤーにはブレンド運動や画面のチラつきが一切見えません。
  2. 既存機能への無影響: 1フレーム後に元の設定へ自動復帰するため、ロード後に通常発生するカメラ移動や、通常時の会話スキップ(Sys_SkipManager)の挙動には一切悪影響を与えません。
  3. 低負荷・シンプル: 既存の管理クラスへ最小限のメソッド追加と1フレーム待機を挟むのみで動作するため、パフォーマンス負荷や他システムとの競合リスクが極めて低いです。

オートモード

ボタン表示の仕様変更

「オートモード」機能自体の実装は、簡単に済ませることができました。
ただ、当初の設計通りのボタンだと、意味をなさないことに気がつき、
「オートボタン」そのものを独立させました。
「スキップボタン」と同じですね。

テキスト設定画面から削除して独立。

そして「オートボタン」を押したら、オンになっていることが視認できるようにスクリプトを組んでもらいました。
デザインも将来変えられるように柔軟な対応をしてくれてます。
同じように「スキップボタン」も改修しました。
スキップボタンを長押ししている間は押されている表示になります。

実機での挙動

修正部分として、
・テキスト設定の「オートウェイト」を「プレビュー」のテキストループの待機秒数に反映。
・各設定画面の開閉に伴う「オートモード」の解除に関しては、「解除しない」としました。
・オートモードをオンにしたまま、セーブロードを行うと、そのままオンになっている問題を解決。

優先挙動としては、
・オート中にスキップが開始されたら、オートは自動オフ。
・スキップ中はオート無視。
・オートはスキップがオフの時のみ開始可能。

プロジェクトまとめ

編集後記

今回の「テキスト設定」の実装は、想定内の実働と想定外の実働がありました。

テキストスピードに関しては、
すでに会話システムに搭載されていた「セリフスピード設定」と被っていたので、それを削除し統一しました。

ウィンドウ透明度は、当初は考えてなかったものです。
設計段階で提案表にあったので、「それ採用!」という具合でいただきました。
(提案しているということはサクッとできそうと、ほくそ笑みました)

設定のリアルタイム反映である「プレビュー」にはちょっと時間がかかりそうと思っていましたが、
案外すんなりいきました。(何回かは修正依頼を行いましたが)

「スキップ」に関しては、独自のCut進行・会話システムのため、
スタンダード演出である「早送り」はできないのではないかと思っていました。
なので、Cut毎の飛び飛びになるかと思いきや、むしろそっちのほうが難しいみたいな感じでした。
「未読・既読」の判定もなんなくこなし、理想的なスキップにしてもらいました。

移動・変化などの「DOTween」使用時の即時停止は、別コストがかかるということでやめました。
スタンダード的にも、演出時間はスキップされないエンジンが多いとのこと。
この先、演出過多になってきたときはスキップする仕様に変えた方が良いかもしれません。

そして「未読・既読」の「色」の変化タイミングであれほど時間を取られるとは思いませんでした。
途中、普通に諦めたのですが、
分からないなりの意見をちょぼちょぼ言っていたら、兆しが見えてきたので、そのまま解決まで強引に持っていきました。
最後はClaudeのファインプレイだったと思います♪

次の実装は、ついに「音量設定」になります。
簡単にいきそうでもあり、難航しそうな気配もあり、ちょっと想定しにくいです。
「ボイス」の実装予定はないですが、
別開発で将来的にはあるとは思うので、ここの基盤も柔軟に作っておくほうが良いですね。

今回完成した機能一覧

✅ テキスト設定UI(UI_TextSettingsPanel)
✅ スキップ機能(すべて・既読のみ)
✅ 既読フラグシステム
✅ 既読・未読の色分け
✅ 既読クリアボタン(確認ダイアログ経由)
✅ 左クリック進行・ボタン優先制御
✅ プレビューのループ文字送り
✅ ウィンドウ透明度リアルタイム反映
✅ オートウェイトとプレビューの連動

✅ オートモードON/OFF切り替え(UI_AutoButton)
✅ メッセージ表示完了後にオートウェイト秒数待機して自動進行
✅ オートウェイト中のクリック割り込み(待機キャンセル→即進行・オート継続)
✅ スキップ中はオート無視
✅ スキップ開始時にオートを自動オフ
✅ 起動時にオートモードをオフにリセット
✅ ロード完了時にオートモードをオフにリセット
✅ オートボタンのON/OFF表示(Image.color直接変更方式)
✅ スプライト差し替えによるON/OFF表示の仕組み(将来対応済み)

スクリプト構成(全体)

【新規作成】
Sys_SkipManager.cs ← スキップ・既読フラグ管理
Sys_TextSettingsManager.cs ← テキスト設定値の読み書き
UI_TextSettingsPanel.cs ← テキスト設定UI画面
UI_SkipButton.cs ← スキップボタン(長押し制御・専用ボタン表示)
UI_PanelBase.cs ← 既存・UI画面の基底クラス
UI_AutoButton.cs ← オートボタン(ON/OFF制御・専用ボタン表示)

【改修】
Sys_Messenger.cs ← 大幅改修(詳細後述)
Sys_Flag_Manager.cs ← GetKeysByPrefix・DeleteKey追加
Sys_SkipManager.cs
Sys_TextSettingsManager.cs
Sys_SaveManager.cs ← セーブシステム管理
UI_SaveLoadButtons.cs ← テキスト設定ボタン追加

Hierarchy構造

【DontDestroyOnLoad・ルート直下】
UI_Root
 ├ Sys_UIRoot.cs
 ├ Sys_InputManager.cs
 ├ Black_Canvas
 ├ SaveLoad_Canvas ← UI_SaveLoadPanel.cs
 ├ Dialog_Canvas ← Sys_DialogManager.cs
 └ TextSettings_Canvas ← UI_TextSettingsPanel.cs
  └ Panel(初期Inactive)
   ├ Title_Text
   ├ Close_Btn
   ├ SkipMode_Section
   │ ├ SkipAll_Btn
   │ ├ SkipRead_Btn
   │ └ ClearRead_Btn ← 既読クリアボタン
   ├ TextSpeed_Section
   │ ├ TextSpeed_Slider
   │ └ TextSpeed_Text
   ├ AutoWait_Section
   │ ├ AutoWait_Slider
   │ └ AutoWait_Text
   ├ WindowAlpha_Section
   │ ├ WindowAlpha_Slider
   │ └ WindowAlpha_Text
   └ Preview_Section
    ├ PreviewWindow(Image)
    └ PreviewMessage

SaveManager ← Sys_SaveManager.cs
TextSettingsManager ← Sys_TextSettingsManager.cs
SkipManager ← Sys_SkipManager.cs
Sys_Flag_Manager(自動生成)

【シーン内】
System_set
 ├ ChapterManager
 ├ CutManager
 └ ScreenshotManager
Dynamic_Canvas
 └ Msg_UI
  └ Msg
  ├ Name_Plate
  ├ Msg_Panel ← 透明度適用対象・messagePanelRect
  ├ Msg_Panel_Wait_Icon
  └ Msg_text ← messageTextアサイン先(しかしデフォルト色(未読色)はMessengerで設定)
TestButton_Canvas(仮)
 ├ SaveButton
 ├ LoadButton
 ├ TextButton
 ├ SkipButton ← UI_SkipButton.csアタッチ
 └ AutoButton ← UI_AutoButton.csアタッチ
CUT_OBJECTs
CAMERA_Main
Cut_001〜(初期Inactive)

SYS_フラグ一覧

【テキスト設定】
SYS_SKIP_MODE int 0=既読のみ 1=すべて デフォルト:0
SYS_TEXT_SPEED int 1〜100 デフォルト:80
SYS_AUTO_MODE bool ON/OFF デフォルト:false
SYS_AUTO_WAIT int 1〜10秒 デフォルト:3
SYS_WINDOW_ALPHA int 0〜100 デフォルト:100

【既読フラグ】
SYS_READ_{sceneName}{cutIndex}{messageIndex}
例:SYS_READ_a01_Novel_1_3_2
→ 表示完了時に記録(ロード中は記録しない)
→ global_system.jsonに永続保存

Sys_TextSettingsManager.csの改修

InitializeDefaults()内のAUTO_MODE初期化を変更:
変更前:
if (!fm.HasKey(KEY_AUTO_MODE)) fm.SetBool(KEY_AUTO_MODE, DEFAULT_AUTO_MODE);
変更後:
fm.SetBool(KEY_AUTO_MODE, DEFAULT_AUTO_MODE); // 毎起動時に強制リセット
理由:
アプリ起動時に常にオフにリセットすることで、
オートONのままアプリを終了→再起動しても意図せず進行しないようにする。

Sys_Messenger.cs(会話システム)の主要設計

■ 色管理

【重要】TMPのVertex Colorとは別管理
public Color unreadColor = EFD8A1(クリーム色)← Inspector指定
public Color readColor = FFAB00(黄色) ← Inspector指定

理由:
TMPのcolorを動的取得すると複数インスタンス・ロードをまたぐ構造で色汚染が起きるため
Inspectorで明示的に指定する方式を採用。
TMPのVertex Colorはエディタ上の見た目確認用として残す。

■ CutIndexの取理

【重要】ShowNextMessage()の先頭で毎回取得する

理由:
MessengerはCut外(Dynamic_Canvas配下)にあるため、OnEnable()はシーンロード時の1回しか呼ばれない。
CutManagerのcurrentCutIndexはCutが進むたびに変わるため、毎回取得する必要がある。

■ 既読フラグの記録タイミング

DisplayMessage()完了時(waitIcon表示の瞬間)に記録。
ロード中(Sys_SaveManager.IsLoading)は記録しない。

■ 進行入力

・左クリック(UI上は無視・EventSystem.IsPointerOverGameObject)
・ホイールダウン
・どちらも isDisplayingMessage || isMessageCompleted 時のみ有効

■ スキップ処理

Update()で毎フレーム判定:
・isDisplayingMessage → isMessageSkipped = true(即表示)
・isMessageCompleted → ShowNextMessage()(自動進行)

■ 改修

【追加フィールド】
private Coroutine autoWaitCoroutine;
【OnEnable()】
autoWaitCoroutine = null; を追加
【Update()】
スキップ判定ブロック内にStopAutoWait()を追加。
スキップ判定ブロックのreturnの後に、オートモード判定を追加:
isMessageCompleted && autoWaitCoroutine == null のときのみ
AutoWaitThenAdvance()を起動する。
【OnNextButtonClick()】
オートウェイト中の割り込みケースを追加:
autoWaitCoroutine != null → StopAutoWait()して即進行。
オートモードは継続(切らない)。
【新規追加メソッド】
IEnumerator AutoWaitThenAdvance()
→ SYS_AUTO_WAIT秒待機後にShowNextMessage()を呼ぶ。
→ 待機完了時点でもisMessageCompleted && AutoModeを再確認する。
void StopAutoWait()
→ autoWaitCoroutineを安全に停止してnullにする。
【SetMessageIndex()】
autoWaitCoroutine = null; を追加。

UI_TextSettingsPanel.csの主要設計

・UI_PanelBase継承(入力ブロック自動管理)
・TextSettings_Canvasにアタッチ(常にActive)
・Panel(子)が初期Inactive
・Open()でRefreshAll()→SYS_フラグの値をUIに反映
・Close()でプレビューCoroutine停止
・プレビューはループ文字送り(オートウェイト時間で待機)
・透明度変更時はFindObjectsByTypeで全Messengerに適用
・既読クリアはSys_DialogManager経由で確認後ClearAllReadFlags()

Sys_SkipManager.csの主要設計

・ルート直下・DontDestroyOnLoad
・IsSkipping:スキップ中フラグ
・StartSkip():IsBlocked中は無効
・CanSkip():IsSkipping + SYS_SKIP_MODE + IsRead()で判定
・MarkAsRead():HasKey()チェックで重複記録防止
・ClearAllReadFlags():GetKeysByPrefix→DeleteKey→SaveGlobal

■ 改修

StartSkip()の先頭に追加:AutoModeがONであればSetAutoMode(false)を呼ぶ。
理由:
スキップ関連の処理はSkipManagerに集約する設計方針のため。
SkipManagerがTextSettingsManagerに書き込む唯一の箇所。

Sys_SaveManager.csの改修

LoadRoutine()のIsLoading = false; の直前に追加:
Sys_TextSettingsManager.Instance?.SetAutoMode(false);
理由:
ゲームプレイ中のロード時はAwake()が再実行されないため、
TextSettingsManagerのInitializeDefaults()によるリセットが走らない。
ロード完了のタイミングで確実にオフにする。

Sys_Flag_Manager.csへの追加メソッド

// プレフィックスで始まる全キーのリストを返す
public List GetKeysByPrefix(string prefix)

// 指定キーを全Dictionaryから削除する
public void DeleteKey(string key)

UI_AutoButton.csの主要設計

・AutoButton GameObjectにアタッチする
・IPointerDownHandlerでクリック検知
→ interactableに触らないためON中でも確実にクリックを受け付ける
・OnPointerDown()でAutoModeをトグル後、即RefreshVisual()を呼ぶ
・Update()でAutoModeの外部変化(スキップによるオフ等)に追従

■ ON/OFF表示の仕組み

・useSprite = (spriteOff != null && spriteOn != null)で自動判定
・スプライト設定済み → Image.spriteを差し替え+button.Select()でON維持
・スプライト未設定(現状)→ Image.colorを直接変更するフォールバック

■ ColorBlockを使わない理由

ColorBlock経由の変更はUnityのUI再描画タイミングに依存するため
即時反映されないケースがあった。Image.colorの直接変更で解決。

■ 将来のデザイン調整

InspectorのSpriteOff・SpriteOnにスプライトを差し込むだけで
自動的にSprite Swap方式に切り替わる。コード変更不要。

設計ルール(継続・追記分)

・キャラクター・カメラはCutの外に置く
・Cutをまたいで使うオブジェクトはCutの外に置く
・SaveManagerはルートオブジェクト必須
・DontDestroyOnLoad対象はルートに置く
・演出のみのCutには必ずCmd_EndCutをアタッチ
・複数Cmd_Move_xyzがある場合EndCutを呼ぶのは最後の1つだけ
・DontDestroyOnLoadはルート直置きのスクリプトのみ記述
・UI_Rootの子・シーン内オブジェクトへの記述は禁止
・将来のUI画面は全てUI_PanelBaseを継承する
・テキスト設定値はSYS_フラグで管理(セーブデータに依存しない)
・既読フラグのキー形式:SYS_READ_{scene}{cut}{msg}
・色管理:TMPのVertex ColorとMessengerのInspector指定は別物

確定した仕様

■ オートモードの動作フロー

  1. 文字送り中(isDisplayingMessage)
    → オートは何もしない。文字送りが終わるのを待つ。
  1. 文字送り完了(isMessageCompleted・waitIcon表示)
    → SYS_AUTO_WAIT秒待機後、ShowNextMessage()を呼ぶ
  1. オートウェイト中に左クリック・ホイールダウン
    → 待機キャンセル→即ShowNextMessage()→オート継続

■ スキップとの優先関係

・スキップ中はオート無視
・オート中にスキップ開始→オート自動オフ(Sys_SkipManager.StartSkip()内で処理)
・オートはスキップOFF時のみ動作

コメント