入力システムの一本化【Unity6】

絶対調味ロジック

ここまでで様々なシステムを作ってきましたが、
現状、マウス操作などが各スクリプトに散在している状態になっています。

将来的にコントローラー対応キーコンフィグなどを設定する際の基盤ができていなかったので、
この段階で入力システムの一本化を行ってみました。

  • 入力の検知場所が複数に分散
  • キーコンフィグ・コントローラー対応が不可能な状態
  • 右クリックの処理がSys_Messenger/UI_SaveLoadPanel/UI_Managerの3箇所に重複

これらの解消を目指します。

入力システムの構築

このシステムのメリット

キー変更が1箇所で完結する
GameInput.inputactionsのBindingを変えるだけで全スクリプトに反映されます。
各スクリプトを個別に修正する必要がありません。

コントローラー対応が容易
GameInput.inputactionsにコントローラーのBindingを追加するだけで対応できます。
各スクリプトはFacade.AdvancePressedを参照しているだけなので、入力デバイスを意識しません。

入力の競合・重複が起きにくい
入力検知の場所がFacade一本に絞られているため「どこで何を検知しているか」が明確になります。

構築したシステムの全体像

 GameInput.inputactions(アクション定義
     ↓
 Sys_InputFacade(入力の窓口・一本化
     ↓
 各スクリプト(Facade経由でのみ入力を取得
     ↓
 Sys_InputRouter(状態に応じた入力の振り分け

各コンポーネントの役割

GameInput.inputactions

UnityInputSystemによるアクション定義ファイルです。

新規Actionファイルを「GameInput」として作成します。
Generate C# Class」にチェックを入れて準備完了しておきます。

GameInput」ファイルをダブルクリックか、「Edit Asset」を押してアクション定義を行います。

表示名意味
Action Mapsグループ(UI / Systemなど)
Actions行動(Next / Cancelなど)
Action Properties詳細設定(入力タイプなど)

ActionMap」でカテゴリ分けをしたら、各「Action」を▼で開いて、操作を適用していきます。

特定のキー「Esc」や「Shift」などを指定したい場合は、検索すると表示されるようになります。

今回は最終的に、グループを「UI」「System」に分け、5つのアクションを定義しました。

UIマップ
 ├ Advance      (左クリック:ADV進行)
 ├ Cancel       (右クリック:キャンセル)
 ├ BacklogClose (右クリック:バックログ閉じ)
 └ ScrollY      (ホイール:スクロール値)

Systemマップ
 └ Quit         (ESC:終了)

Sys_InputFacade

全スクリプトの入力取得の唯一の窓口になります。
「Mouse.current」や「Keyboard.current」を直接触るのはこのクラスのみに限定。
各スクリプトはFacadeのプロパティを参照するだけでよい。

Sys_InputRouter

ADV中・UI中などの状態(Context)を管理し、同じ入力でも状態によって異なる処理に振り分ける責務を持つ。ContextはUI_Manager側から自動的に切り替わる。

Sys_InputInitializer

起動時にnew GameInput()でアクションを生成しFacadeに渡します。
アサインフィールド不要のシンプルな構成。

InputSystem_Root

上記コンポーネントの親オブジェクトにアタッチ。
DDOL(DontDestroyOnLoad)として全シーンで入力システムを維持する。

右クリックの責任分離

今回の核心部分。
右クリックは「状態依存の入力」として以下のように整理しました。

右クリック
 ├ UIが開いている → UI_Managerがstackのトップを閉じる
 ├ バックログ中   → UI_PanelBase継承により自動的にClose()
 └ ADV中         → UI_ManagerがMenuPanelを開く

この判断はすべて「UI_Manager」が担い、各スクリプトは右クリックを意識しません。

UI_PanelBaseとの連携

全UIパネルの基底クラス「UI_PanelBase」のOpen/Closeが
Sys_InputManager」のblock/unblockと自動連携しています。

Open()  → Sys_InputManager.Block()   → ADV進行を止める
Close() → Sys_InputManager.Unblock() → ADV進行を再開
全Close → Sys_InputRouter.SetContext(Main) → 状態をADVに戻す

Sys_BacklogManager(バックログ)」も今回「UI_PanelBase」を継承させたことで、
他のUIと完全に同一の仕組みに乗りました。

修正した既存の問題点

UI_PanelBase.Close()の誤ったContext設定
Close()のたびにContextがUIに戻る実装になっていました。
これを削除し、全UIが閉じた時点でUI_Manager.Unregister()がMainに戻す正しい設計に修正。

Missing参照問題
タイトルシーンの「おわる」ボタンが
DDOLの「Sys_QuitManager」をInspectorの「OnClick」に直接登録していたため、
シーン遷移後にMissingになっていた。
UI_TitleManagerのStart()でInstance経由のAddListenerに変更して解決。

DDOLオブジェクトへのOnClick登録は必ずコード経由(Instance参照)が原則。

将来的な拡張指針

キーコンフィグUI
GameInput.inputactionsのBindingはランタイムでも変更可能です。
設定画面からキー割り当てを変更する機能を追加できます。

コントローラー対応
InputActionsにGamepad用Bindingを追加するだけで既存スクリプトへの影響なしに対応できます。

新しい入力アクションの追加手順
 1.GameInput.inputactionsにActionを追加
 2.Sys_InputFacadeにプロパティを追加
 3.使う側のスクリプトでFacade経由で参照

ADV以外のシーンへの対応
Sys_InputRouterのContextを拡張することで、
ミニゲームや別システムへの入力切り替えも同一の仕組みで管理できる。

スキップを「Ctrl」キーに登録

InputActions UIマップに追加

Actionに「Skip」を追加し、Ctrlキーを割り当てます。

Sys_InputFacade Contextを追加

public bool SkipDown => input != null && input.UI.Skip.WasPressedThisFrame();
public bool SkipUp => input != null && input.UI.Skip.WasReleasedThisFrame();

Sys_InputRouter ADV入力処理

void HandleADVInput(Sys_InputFacade input)
{
    if (Sys_InputManager.Instance != null &&
        Sys_InputManager.Instance.IsBlocked)
        return;

    if (input.SkipDown)
        Sys_SkipManager.Instance?.StartSkip();

    if (input.SkipUp)
        Sys_SkipManager.Instance?.StopSkip();
}

最終完成形(全体構造)

 [InputActions]
    ↓ Ctrl
 [Sys_InputFacade]
    ↓ SkipDown / SkipUp
 [Sys_InputRouter(ADVのみ)]
    ↓ StartSkip / StopSkip
 [Sys_SkipManager]
    ↓ CanSkip判定
 [Sys_Messenger]
    ↓ 表示制御(全文表示・次へ)

 ✔ InputActionsにCtrl追加
 ✔ FacadeにSkipDown/Up追加
 ✔ RouterにADV入力処理追加
 ✔ SkipManagerはそのまま
 ✔ Messengerもそのまま

オートを「Alt」キーに登録

InputActions UIマップに追加

Actionに「Auto」を追加し、Altキーを割り当てます。

Sys_InputFacade Contextを追加

public bool AutoPressed => input != null && input.UI.Auto.WasPressedThisFrame();

Sys_InputRouter ADV入力処理

void HandleADVInput(Sys_InputFacade input)
{
    if (Sys_InputManager.Instance != null &&
        Sys_InputManager.Instance.IsBlocked)
        return;

    // --- スキップ(Ctrl)---
    if (input.SkipDown)
        Sys_SkipManager.Instance?.StartSkip();

    if (input.SkipUp)
        Sys_SkipManager.Instance?.StopSkip();

    // --- オート(Alt:トグル)---
    if (input.AutoPressed)
    {
        if (Sys_TextSettingsManager.Instance != null)
        {
            bool next = !Sys_TextSettingsManager.Instance.AutoMode;
            Sys_TextSettingsManager.Instance.SetAutoMode(next);
        }
    }
}

動作仕様

Alt動作

  • Alt押す(1回目)→ AutoMode = true:Alt押す(1回目)
  • Alt押す(2回目)→ AutoMode = false:停止

Ctrlとの関係(重要)

Ctrl:瞬間動作(スキップ)
Alt :状態保持(トグル)

コメント