【Unity】Unity New Input System入門:次世代の入力処理で開発を加速させる

作成: 2026-02-05最終更新: 2026-07-13

UnityのNew Input Systemの使い方を解説。Input Actions、Binding、Player Inputコンポーネントを使って、マルチプラットフォーム対応の入力処理を実装できます。

「キーボードでもゲームパッドでも操作できるようにしたい」「プレイヤーがキー配置を変更できるようにしたい」――こんな要望を実現しようとして、旧Input Systemで苦労した経験はありませんか?

New Input System は、Unity 2019から導入された次世代の入力処理システムです。 Input Actions という抽象化された概念を使って、デバイスに依存しない柔軟な入力処理を実現します。

New Input Systemのイメージ。キーボード、ゲームパッド、スマートフォンの入力がひとつのハブに集約される

この記事でわかること

  • Input Actionsの仕組み(デバイス非依存の入力処理)
  • Input Actions Assetの作成からスクリプト実装までの流れ
  • Player Input方式とC# Class生成方式の使い分け
  • 「移動が止まらない」などの定番トラブルの解決方法

動作確認環境: Unity 2022.3 LTS / Unity 6

Sponsored

インストールと初期設定

パッケージのインストール

  1. Window > Package Managerを開く
  2. 左上のドロップダウンからUnity Registryを選択
  3. Input Systemを検索してインストール
  4. 再起動を促すダイアログが表示されたら「Yes」をクリック

Active Input Handlingの設定

  1. Edit > Project Settings > Playerを開く
  2. Other Settings > Configuration > Active Input Handlingを確認
  3. Both または Input System Package (New) を選択
  4. エディタが再起動される

Both: 旧Input Systemと新Input Systemの両方が使用可能。移行期間中に便利です。

旧Input Systemとの違い

旧Input Systemの問題点

  • デバイスごとに個別のコードが必要
  • キーバインドのカスタマイズが困難
  • マルチプラットフォーム対応が大変
  • ローカルマルチプレイヤー対応が困難

New Input Systemの利点

  • デバイス非依存の実装 - 同じコードで複数デバイスに対応
  • マルチプラットフォーム対応 - 設定ファイルで一元管理
  • キーバインドのカスタマイズ - 標準機能として提供
  • ローカルマルチプレイヤー対応 - Player Inputコンポーネントで簡単に実装
  • 複雑な入力パターン - 長押し、ダブルクリックなどを設定だけで実現

新旧の構造の違いを図にすると、New Input Systemの狙いがよく分かります。デバイスごとにバラバラだった入力処理が、「Action」という1つの窓口に集約されるのです。

旧Input SystemとNew Input Systemの比較図。旧はデバイスごとに個別のコードが必要だったが、新はすべてのデバイスがActionに集約され1つのコードで動く

New Input Systemの構成要素

要素説明
Input Actions Asset入力設定を保存するファイル
Action Map関連するActionをグループ化したもの
Action操作の単位(移動、ジャンプなど)
BindingActionと実際の入力デバイスを結びつけるもの
Player Input入力イベントを受け取るコンポーネント
Input Actionsの構成図。キーボードのWASDとゲームパッドの左スティックがBindingで「Move」Actionに結びつき、コードはActionだけを見ればよい

Action Type

タイプ用途
Value連続的な値(移動スティック、トリガーなど)
Buttonボタンの押下状態(ジャンプ、攻撃など)
Pass-through入力をそのまま伝える(複数入力点の処理)
Sponsored

Input Actionsの作成と設定

Input Actions Assetの作成

  1. Projectウィンドウで右クリック
  2. Create > Input Actionsを選択
  3. アセットをダブルクリックして編集ウィンドウを開く

Action Mapの作成

左上の+ボタンをクリックして、新しいAction Mapを追加(例:「Player」)

Actionの作成

  1. Action Mapを選択した状態で+ボタンをクリック
  2. Action名を入力(例:「Move」「Jump」)
  3. Action TypeとControl Typeを設定

Bindingの追加

WASDキーの設定例:

  1. 「Move」Actionを選択
  2. +ボタンからAdd Up/Down/Left/Right Compositeを選択
  3. Up: W、Down: S、Left: A、Right: Dを設定

ゲームパッド左スティックの追加:

  1. 同じ「Move」Actionを選択
  2. +ボタンからAdd Bindingを選択
  3. PathでGamepad > Left Stickを選択

マルチデバイス対応: 同じActionに複数のBindingを追加することで、キーボードでもゲームパッドでも同じコードで動作します。

C# Classの生成(推奨)

Input Actions Assetからタイプセーフなクラスを自動生成できます。

そもそも「タイプセーフ」とは?: 「型(タイプ)によって安全が守られている」という意味です。Actionを"Move"のような 文字列 で指定する書き方だと、"Mvoe"とタイプミスしてもコンパイルは通ってしまい、実行して動かなくなって初めてミスに気づきます。一方、生成したクラスを使えばinputActions.Player.Moveのように コードの一部 として書けるため、ミスした瞬間にエディタがエラーで教えてくれて、入力補完(インテリセンス)も効きます。これが「タイプセーフ」の恩恵です。

タイプセーフの意味を表す比較図。文字列指定はタイプミスしてもコンパイルが通ってしまい実行するまで気づけないが、生成クラスなら書いた瞬間にエディタがエラーで教えてくれる
  1. ProjectウィンドウでInput Actions Assetを選択
  2. Inspectorで Generate C# Class にチェック
  3. Class Name(例:PlayerInputActions)を設定
  4. 必要に応じてNamespaceを設定
  5. Apply をクリック

これにより、PlayerInputActions.csが生成され、new PlayerInputActions()でインスタンス化できるようになります。

スクリプトでの実装

方法の使い分け

方法適したケース
Player Input + Unity Events小規模プロジェクト、プロトタイプ、プログラマー以外も設定を変更する場合
C# Class生成中〜大規模プロジェクト、タイプセーフな実装が必要な場合、複雑な入力処理

注意(二重有効化): Player Inputコンポーネント方式と、生成C#クラスをnewする方式を、 同じActionアセットで併用しない でください。同じActionが二重に有効化されてコールバックが2回ずつ飛び、「入力が2連発する」バグの定番原因になります。プロジェクト内でどちらか一方に統一しましょう。

方法1: Player Input + Unity Events

Player Inputコンポーネントを使うと、インスペクターでActionとメソッドを紐づけることができます。

Player Inputコンポーネントの設定

  1. GameObjectにPlayer Inputコンポーネントを追加
  2. ActionsにInput Actions Assetを設定
  3. BehaviorUnity Events に変更
  4. Eventsセクションが表示される
  5. 各Actionに対応するメソッドを登録
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    private Vector2 moveInput;

    public void OnMove(InputAction.CallbackContext context)
    {
        moveInput = context.ReadValue<Vector2>();
    }

    public void OnJump(InputAction.CallbackContext context)
    {
        if (context.performed)
        {
            Debug.Log("ジャンプ!");
        }
    }
}

方法2: C# Class生成(推奨)

この方法のコードには+=という見慣れない記号が登場します。これは イベント登録(購読) と呼ばれる仕組みで、「このActionが発生したら、私のこのメソッドを呼んでください」とあらかじめ申し込んでおくイメージです。一度登録すれば、あとは入力が発生するたびにUnity側から自動でメソッドが呼ばれます。逆に-=は「もう呼ばなくていいです」という登録の解除です。

イベント登録の仕組みの図。+=でMove ActionにOnMoveメソッドを登録しておくと、入力が発生したときに登録したメソッドが自動で呼び出される。-=で登録を解除できる
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    private PlayerInputActions inputActions;
    private Vector2 moveInput;

    void Awake()
    {
        inputActions = new PlayerInputActions();
        inputActions.Player.Move.performed += OnMove;
        inputActions.Player.Move.canceled += OnMove;
        inputActions.Player.Jump.performed += OnJump;
    }

    void OnEnable() => inputActions.Enable();
    void OnDisable() => inputActions.Disable();

    void OnDestroy()
    {
        // イベントハンドラの解除(メモリリーク防止)
        inputActions.Player.Move.performed -= OnMove;
        inputActions.Player.Move.canceled -= OnMove;
        inputActions.Player.Jump.performed -= OnJump;
        inputActions.Dispose();
    }

    private void OnMove(InputAction.CallbackContext context)
    {
        moveInput = context.ReadValue<Vector2>();
    }

    private void OnJump(InputAction.CallbackContext context)
    {
        Debug.Log("ジャンプ!");
    }
}

メモリリーク防止: OnDestroyでイベントハンドラを-=で解除し、Dispose()を呼び出すことで、オブジェクト破棄時のメモリリークを防げます。

ところで、なぜMoveにはperformedcanceled両方 に登録しているのでしょうか。Actionのイベントには主にstarted(入力が始まった)・performed(入力が成立した)・canceled(入力が終わった)の3つのタイミングがあります。移動のように「押している間ずっと値が欲しい」操作では、performedだけだと スティックを離しても最後の値が残って動き続けて しまいます。canceledにも登録しておくと、離した瞬間にVector2.zeroが届いてピタッと止まれるのです。

performedとcanceledのタイムライン図。スティックを倒している間はperformedが値を届け、離した瞬間にcanceledが発生して入力ゼロが届き、移動が止まる

Action Mapの切り替え

ゲームプレイ中とUI操作中で、有効なAction Mapを切り替えることができます。

public class InputManager : MonoBehaviour
{
    private PlayerInputActions inputActions;

    void Awake()
    {
        inputActions = new PlayerInputActions();
    }

    // ゲームプレイ中:PlayerマップをON、UIマップをOFF
    public void EnableGameplayInput()
    {
        inputActions.Player.Enable();
        inputActions.UI.Disable();
    }

    // UI操作中:UIマップをON、PlayerマップをOFF
    public void EnableUIInput()
    {
        inputActions.Player.Disable();
        inputActions.UI.Enable();
    }
}

活用例: ポーズメニューを開いたらUIマップに切り替え、閉じたらPlayerマップに戻す。これにより、メニュー操作中にキャラクターが動くことを防げます。

Sponsored

InteractionsとProcessors

Interactions

Interaction用途
Hold長押しで発火
Tap短いタップを検知
Multi Tapダブルクリックなどの連続タップを検知

Processors

Processor用途
Normalizeベクトルを正規化(斜め移動の速度補正)
Invert入力値を反転
Scale入力値をスケーリング(感度調整)
Dead Zoneスティックのわずかな傾きを無視

デバイスの接続/切断検知

ゲームパッドの抜き差しなど、デバイスの状態変化を検知できます。

using UnityEngine;
using UnityEngine.InputSystem;

public class DeviceManager : MonoBehaviour
{
    void OnEnable()
    {
        InputSystem.onDeviceChange += OnDeviceChange;
    }

    void OnDisable()
    {
        InputSystem.onDeviceChange -= OnDeviceChange;
    }

    void OnDeviceChange(InputDevice device, InputDeviceChange change)
    {
        switch (change)
        {
            case InputDeviceChange.Added:
                Debug.Log($"デバイス接続: {device.displayName}");
                break;
            case InputDeviceChange.Removed:
                Debug.Log($"デバイス切断: {device.displayName}");
                break;
        }
    }
}

活用例: ゲームパッドが切断されたらポーズ画面を表示する、接続されたら「コントローラーを検出しました」と通知するなど。

Sponsored

実践:1人のプレイヤーを最後まで組む

部品の説明はそろいました。ここからは、3Dアクションの主人公、2Dプラットフォーマーの自機、見下ろしARPGの操作キャラ——どのジャンルでも必要になる 「1人のプレイヤー入力一式」 を1本のスクリプトに組み上げます。内容は、①移動+ジャンプ、②ポーズでPlayer⇄UIのMap切替、③ゲームパッドが抜かれたら自動ポーズ、の3点セットです。

1人のプレイヤー入力一式の図。Playerマップで移動とジャンプを受け取り、ポーズボタンでUIマップへ切り替わり、ゲームパッドが抜かれた瞬間は自動でポーズが開く

Input Actions側の準備: PlayerマップにMove(Vector2)・Jump(Button)・Pause(Button、Escキーとパッドのメニューボタン)、UIマップにResume(Button)を作り、C# Classを生成しておきます。

using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(CharacterController))]
public class OnePlayerController : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private GameObject pausePanel; // ポーズ画面のUI

    private PlayerInputActions input;
    private CharacterController controller;
    private Vector2 moveInput;

    void Awake()
    {
        controller = GetComponent<CharacterController>();
        input = new PlayerInputActions();

        // 購読はAwakeで登録し、OnDestroyで解除(対で管理する)
        input.Player.Move.performed += OnMove;
        input.Player.Move.canceled += OnMove;
        input.Player.Jump.performed += OnJump;
        input.Player.Pause.performed += OnPause;
        input.UI.Resume.performed += OnResume;
    }

    void OnEnable()
    {
        // 有効化はOnEnable/OnDisableで対にする
        input.Player.Enable();
        InputSystem.onDeviceChange += OnDeviceChange;
    }

    void OnDisable()
    {
        input.Disable();
        InputSystem.onDeviceChange -= OnDeviceChange;
    }

    void OnDestroy()
    {
        input.Player.Move.performed -= OnMove;
        input.Player.Move.canceled -= OnMove;
        input.Player.Jump.performed -= OnJump;
        input.Player.Pause.performed -= OnPause;
        input.UI.Resume.performed -= OnResume;
        input.Dispose();
    }

    void Update()
    {
        // 移動(簡易版。カメラ基準にしたい場合はCharacterControllerの記事参照)
        Vector3 move = new Vector3(moveInput.x, 0f, moveInput.y);
        controller.SimpleMove(move * moveSpeed);
    }

    private void OnMove(InputAction.CallbackContext ctx) => moveInput = ctx.ReadValue<Vector2>();
    private void OnJump(InputAction.CallbackContext ctx) => Debug.Log("ジャンプ!");
    private void OnPause(InputAction.CallbackContext ctx) => OpenPause();
    private void OnResume(InputAction.CallbackContext ctx) => ClosePause();

    private void OpenPause()
    {
        pausePanel.SetActive(true);
        input.Player.Disable(); // 操作の主導権をUIマップへ
        input.UI.Enable();
        Time.timeScale = 0f;
    }

    private void ClosePause()
    {
        pausePanel.SetActive(false);
        input.UI.Disable();
        input.Player.Enable();
        Time.timeScale = 1f;
    }

    private void OnDeviceChange(InputDevice device, InputDeviceChange change)
    {
        // ゲームパッドが抜かれた瞬間に自動ポーズ(操作不能の棒立ちで死ぬのを防ぐ)
        if (device is Gamepad && change == InputDeviceChange.Removed && !pausePanel.activeSelf)
        {
            OpenPause();
        }
    }
}

再生して確認してみてください。WASD/左スティックで移動、Spaceでジャンプ、Escでポーズが開いて キャラが動かなくなり(Playerマップが無効)、Resumeで戻る。そしてプレイ中にゲームパッドのUSBを抜くと、その瞬間にポーズ画面が開きます——市販ゲームで見るあの挙動です。

ポイントは2つです。 「購読はAwake/OnDestroy、 有効化はOnEnable/OnDisableで、それぞれ対にする」(登録しっぱなし・有効化しっぱなしを構造で防ぐ)と、 「Map切替をポーズの開閉と1対1で結びつける」Time.timeScaleだけのポーズは入力が素通りします。Mapごと切り替えるから「ポーズ中に攻撃が暴発」しない。ポーズの全体像は ポーズ実装の記事 参照)。

よくある問題とトラブルシューティング

入力が反応しない

  • OnEnableinputActions.Enable()を呼び出しているか確認
  • Action Mapが有効になっているか確認
  • Bindingが正しく設定されているか確認
  • ビルドに含まれていない場合: Input Actions Assetの設定を確認(下記参照)

Input Actions Assetがビルドに含まれない

Input Actions Assetをビルドに含めるには、以下のいずれかの方法を使用します:

  1. Player Inputコンポーネントで参照する - GameObjectにアタッチしたPlayer Inputコンポーネントで参照
  2. スクリプトから参照する - [SerializeField]でアセットを参照
  3. Addressablesに登録する - Addressable Assetとして管理

注意: Input System 1.1以前にあった「Preload」チェックボックスは廃止されています(Unity 2021以降)。上記の方法でアセットを参照してください。

移動が止まらない

canceledイベントも登録する:

inputActions.Player.Move.performed += OnMove;
inputActions.Player.Move.canceled += OnMove;

なぜ同じメソッドでいい?: canceledイベント発火時、context.ReadValue<Vector2>()は自動的にVector2.zeroを返します。そのため、performedcanceledで同じOnMoveメソッドを使用しても、入力がないときは正しくVector2.zeroが設定されます。

斜め移動が速い

  • Composite BindingのModeを「Digital Normalized」に設定
  • または「Normalize Vector 2」Processorを追加

おまけ:先に知っておくと良いこと

New Input Systemに慣れてきたら、次はこのあたりが視野に入ってきます。

  • キーコンフィグ(リバインド)の実装: プレイヤーが自分でキー配置を変更できる機能は、PerformInteractiveRebinding()というAPIで実現できます。New Input Systemを選ぶ大きな理由のひとつです。
  • 旧Input Systemの知識も無駄にならない: 既存プロジェクトやアセットストアのコードでは旧Inputクラスがまだまだ現役です。旧Input Systemの記事 で両方を知っておくと移行やデバッグに強くなります。
  • 入力を読むタイミングの理解: 入力の検知とキャラクター移動の実装には、UpdateFixedUpdateの使い分けの理解が欠かせません。Update vs FixedUpdateの記事 を合わせて読むのがおすすめです。
  • 公式ドキュメントも頼れる情報源: InteractionsやProcessorsの全種類など、細かい仕様は Unity公式のInput Systemドキュメント が最も正確です。

まとめ

New Input Systemは、学習コストが高いものの、それに見合う強力な機能を提供します。

  • Input Actions でデバイス非依存の入力処理
  • Action Map で状況に応じた入力切り替え
  • Binding で複数デバイスに同じActionを割り当て
  • InteractionsとProcessors で複雑な入力パターンを設定だけで実現
  • C# Class生成 でタイプセーフな実装

まずは簡単な移動とジャンプから始めて、徐々に複雑な機能に挑戦してみてください。