【Unity】Input Actionをスクリプトから扱う方法まとめ

こじゃらこじゃら

Input SystemからActionをスクリプトから扱う方法が色々あってよく分からないの…

このはこのは

それぞれ特徴があるので一通り解説していくね。最終的にはInputActionインスタンスを生成して使うことになるわ。

Input Systemの入力をAction経由で取得する手段は幾つか存在します。

最終的には直接または(ラッパークラス等で)間接的にInputActionクラスのインスタンスを生成し、ここから各種入力を受け取る流れになります。

主な方法は次の通りです。

Actionから入力を取得する方法
  • Input Action Assetのラッパークラスを生成して使う
  • Player Inputコンポーネント経由で入力を受け取る
  • InputActionをスクリプト内部で生成して使う
  • InputActionをインスペクターから設定して使う
  • InputActionReferenceクラスを使う
  • InputActionProperty構造体を使う

このように、Input Actionからの入力方法は多岐に渡ります。

本記事では、上記のAction経由で入力を取得する方法および特徴について一通り解説していきます。

スポンサーリンク

前提条件

事前にInput Systemパッケージがインストールされ、有効化されているものとします。

ここまでの手順が分からない方は、以下記事を参考にセットアップを進めてください。

また、本記事で解説する内容は全てInput Action経由で入力を取得するものとします。

Input Actionの基本的な仕組みや使い方は以下記事で解説しています。

Input Action Assetのラッパークラスを生成して使う

Input Actionは通常、Input Action AssetでMapやActionを定義して使うことが多いでしょう。

Input Action Assetは拡張子が.inputactionsのアセットファイルで、開くと次のようなウィンドウで編集できるActionの定義情報です。

この中のActionへアクセスしたい場合、Input Action Assetのラッパークラスを自動生成してスクリプトから使うことができます。

この方法には次の特徴があります。

主な特徴
  • Componentを使用しないピュアなC#クラスとして扱える
  • 各種Actionにはプロパティとしてアクセスする
  • 上記プロパティは内部的にキャッシュされているのでアクセスが高速
  • Input Action Assetを更新する度にクラスが自動生成される

コードを自動生成する

前準備として、Input Action Assetを予め作成しておく必要があります。

作成方法が分からない方は、以下記事を参考に.inputactionsファイルを作成し、必要なMapやActionを定義してください。

例では、TestInputsという名前のInput Action Assetファイルを作成するものとします。

作成した後、Input Action Assetファイルを選択するとインスペクターよりコード生成するか否かを選択できます。

Generate C# Class項目にチェックを入れ、その下に現れる次の項目を設定した後、Applyボタンをクリックするとラッパークラスを定義したコードが自動生成されます。

  • C# Class File – ファイルパス
  • C# Class Name – ラッパークラス名
  • C# Class Namespace – ラッパークラスの名前空間

注意

ラッパークラスの生成を有効化すると、Input Action Assetが更新される度に再生成処理が走り、再コンパイル処理が走ります。

この時、Input Action Assetファイルを移動すると自動生成されるソースファイルが重複して作られてしまい、コンパイルエラーとなりますのでご注意ください。

また、後述するサンプルスクリプトで使用するラッパークラス名や名前空間は、それぞれC# Class Name、C# Class Namespace項目で設定した名前と一致させる必要があります。

サンプルスクリプト

ラッパークラスを生成したら、これをスクリプトからインスタンス化して入力を参照するスクリプトを実装します。

以下、「Player」というMap配下の「Fire」というActonの入力をチェックし、performedコールバックが発火したらログ出力する例です。

GenerateCodeExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class GenerateCodeExample : MonoBehaviour
{
    // InputActionAssetのラッパークラス
    // 自動生成されたクラス名にする必要がある
    private TestInputs _inputs;

    private void Awake()
    {
        // InputActionAssetのラッパークラスをインスタンス化
        _inputs = new TestInputs();

        // InputActionのコールバックの設定
        // 「Player」はMap名
        // 「Fire」はAction名
        // Map名とAction名は必ずInputActionAssetで設定した名前にする必要がある
        _inputs.Player.Fire.performed += OnFire;
    }

    private void OnDestroy()
    {
        // InputActionのコールバックの解除
        _inputs.Player.Fire.performed -= OnFire;

        // InputActionAssetのラッパークラスの破棄
        // IDisposableを実装しているので、Disposeする必要がある
        _inputs.Dispose();
    }

    private void OnEnable()
    {
        // 全体のActionを有効化
        _inputs.Enable();
    }

    private void OnDisable()
    {
        // 全体のActionを無効化
        _inputs.Disable();
    }

    private void OnFire(InputAction.CallbackContext context)
    {
        print("Fire Actionが呼ばれた!");
    }
}

上記をGenerateCodeExample.csという名前で保存し、適当なゲームオブジェクトにアタッチすると機能するようになります。

もしラッパークラス名やMap名、Action名が異なる場合は適宜設定したものに修正してください。

実行結果

Fireという名前のActionのperformedコールバックが発火するとログ出力されるようになります。

スクリプトの説明

ラッパークラスは生成した後に破棄する必要があるため、フィールドなどに保持しておきます。

// InputActionAssetのラッパークラス
// 自動生成されたクラス名にする必要がある
private TestInputs _inputs;

ラッパークラスはnewでインスタンス化します。

// InputActionAssetのラッパークラスをインスタンス化
_inputs = new TestInputs();

一度インスタンス化したラッパーは、最終的に必ずDisposeメソッドを呼んで破棄する必要があります。これは、ラッパークラスがIDisposableインタフェースを実装しているためです。

// InputActionAssetのラッパークラスの破棄
// IDisposableを実装しているので、Disposeする必要がある
_inputs.Dispose();

ラッパークラス内のActionへのアクセスは、[インスタンス].[Map名].[Action名]といった形でプロパティを見ればよいです。

// InputActionのコールバックの設定
// 「Player」はMap名
// 「Fire」はAction名
// Map名とAction名は必ずInputActionAssetで設定した名前にする必要がある
_inputs.Player.Fire.performed += OnFire;

上記の例では「Player」というMap名の下の「Fire」というAction名のActionのperformedコールバックを受け取る設定を行っています。

Tips

各種Actionへのプロパティを参照する際は、次のようなコードで内部的にActionへの参照がキャッシュされたものを用いています。

// Player
m_Player = asset.FindActionMap("Player", throwIfNotFound: true);
m_Player_Move = m_Player.FindAction("Move", throwIfNotFound: true);
m_Player_Look = m_Player.FindAction("Look", throwIfNotFound: true);
m_Player_Fire = m_Player.FindAction("Fire", throwIfNotFound: true);

・・・(中略)・・・

public InputAction @Move => m_Wrapper.m_Player_Move;
public InputAction @Look => m_Wrapper.m_Player_Look;
public InputAction @Fire => m_Wrapper.m_Player_Fire;

そのため、FindActionメソッドによる検索を回避するために取得側が変数などにキャッシュしておく必要はありません。

最終的にActionから入力を受け取るためには、Enableメソッドで有効化する必要があります。

private void OnEnable()
{
    // 全体のActionを有効化
    _inputs.Enable();
}

無効化したい場合はDisableメソッドを使います。

private void OnDisable()
{
    // 全体のActionを無効化
    _inputs.Disable();
}

そして、入力を受け取った際に次のコードでログ出力しています。

private void OnFire(InputAction.CallbackContext context)
{
    print("Fire Actionが呼ばれた!");
}

Player Input経由で入力を取得する

前述のInput Action Assetは、Player Inputコンポーネントを通じて取得することも可能です。

参考:Class PlayerInput | Input System | 1.5.1

特徴は次の通りです。

主な特徴
  • Componentとしてゲームオブジェクトにアタッチし、設定して使用する
  • InputActionを完全にラップし、各種スクリプトへの通知が可能
  • これにより、スクリプト側からInputActionやPlayerInputクラスを直接参照しなくて良くなる
  • 入力に応じてControl Scheme単位でActionを自動的に有効化・無効化できる
  • 複数プレイヤーの管理(ローカルマルチ)に対応している

Player Inputの使い方の詳細は以下記事で解説しています。

設定の流れ

適当なゲームオブジェクトにPlayer Inputコンポーネントをアタッチし、Actions項目に該当のInput Action Assetを指定します。

次に、Player Inputから入力を受け取るスクリプトを実装します。

今回はFireというActionのみ実装し、performedコールバックを受け取ったらログ出力します。例ではUnityEventを通じてPlayer Inputから入力を受け取る場合を想定します。

PlayerInputExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerInputExample : MonoBehaviour
{
    public void OnFire(InputAction.CallbackContext context)
    {
        // started、canceled、performedの3つのコールバックがある
        // performedコールバックのみ拾う
        if (!context.performed) return;

        print("Fire Actionが呼ばれた!");
    }
}

上記をPlayerInputExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチします。

Player InputのBehaviour項目Invoke Unity Eventsに設定し、Events配下の該当するActionにコールバックを指定します。

実行結果

一つ目の例と同様にFire Actionに該当するボタンが押されたらログ出力されるようになりました。

スクリプトの説明

実質的な処理は、以下のActionをコールバックで受け取る処理のみです。

public void OnFire(InputAction.CallbackContext context)
{
    // started、canceled、performedの3つのコールバックがある
    // performedコールバックのみ拾う
    if (!context.performed) return;

    print("Fire Actionが呼ばれた!");
}

Actionの有効化や破棄などの管理はすべてPlayer Input側で管理してくれます。

コールバックはstarted、prformed、canceledの3種類があるため、Performedのみ拾いたい場合はcontext.performedcontext.phaseプロパティなどでフィルタリングする必要があります。

InputActionを直接スクリプトから生成して使う

ここまではInput Action Asset内部のActionに対して、ラッパークラスを生成したり、Player Inputコンポーネントを経由したりして入力を取得していました。

Input Action Assetを用いずとも単体のActionをスクリプトなどから生成して使うことも可能です。

コーディング量は多いですが、その分小回りが効く実装が可能です。

主な特徴
  • GUIを使わずスクリプトベースで設定がすべて完結する
  • 生成と破棄の管理を全てスクリプト側が責任を持って行う必要がある
  • 柔軟性が高い

サンプルスクリプト

以下、実装例です。

FromScriptExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class FromScriptExample : MonoBehaviour
{
    // スクリプト側から動的生成されるAction
    private InputAction _inputAction;

    private void Awake()
    {
        // InputActionの生成
        // 「Fire」という名前のボタンタイプのInputActionを生成
        // 「<Keyboard>/space」はスペースキーを表す
        _inputAction = new InputAction("Fire", InputActionType.Button, "<Keyboard>/space");

        // InputActionのコールバックの設定
        _inputAction.performed += OnPerformed;
    }

    private void OnDestroy()
    {
        // InputActionのコールバックの解除
        _inputAction.performed -= OnPerformed;

        // InputActionの破棄
        _inputAction.Dispose();
    }

    private void OnEnable()
    {
        // InputActionの有効化
        _inputAction.Enable();
    }

    private void OnDisable()
    {
        // InputActionの無効化
        _inputAction.Disable();
    }

    // InputActionのコールバック
    private void OnPerformed(InputAction.CallbackContext context)
    {
        Debug.Log("Performedが呼ばれた!");
    }
}

上記をFromScriptExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチすると機能するようになります。

実行結果

キーボードのスペースキーを押すたびにログ出力されます。

スクリプトの説明

以下処理でInputActionインスタンスパラメータ指定で生成しています。

// InputActionの生成
// 「Fire」という名前のボタンタイプのInputActionを生成
// 「<Keyboard>/space」はスペースキーを表す
_inputAction = new InputAction("Fire", InputActionType.Button, "<Keyboard>/space");

Bindingに指定するControl PathProcessorInteractionなどは文字列で指定します。

参考:Class InputAction | Input System | 1.5.1

そして、以下コードでperformedコールバックを購読しています。

// InputActionのコールバックの設定
_inputAction.performed += OnPerformed;

そして、スクリプト自身が有効化されるタイミングなどで忘れずにInputActionを有効化します。

private void OnEnable()
{
    // InputActionの有効化
    _inputAction.Enable();
}

注意

InputActionの有効化を忘れると、入力を一切受け取れなくなりますのでご注意ください。

スクリプトが無効化された時は入力を受け取らないようにするため、以下処理でInputActionを無効化しています。

private void OnDisable()
{
    // InputActionの無効化
    _inputAction.Disable();
}

そして、スクリプトが破棄されるタイミングでInputActionインスタンスをDisposeで破棄します。

private void OnDestroy()
{
    // InputActionのコールバックの解除
    _inputAction.performed -= OnPerformed;

    // InputActionの破棄
    _inputAction.Dispose();
}

注意

InputActionクラスはIDisposableインタフェースを実装しているため、必ずDisposeメソッドを呼び出して破棄するようにしてください。

InputActionを直接シリアライズして使う

InputActionクラスはシリアライズ可能なため、[SerializeField]属性を指定してインスペクターから編集させることが可能です。

主な特徴
  • 柔軟性が高い
  • Actionの設定はGUIベースで行える

サンプルスクリプト

以下、InputActionインスタンスをインスペクターから設定可能にしたスクリプトの例です。

FromInspectorExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class FromInspectorExample : MonoBehaviour
{
    // インスペクターから設定可能にする
    [SerializeField] private InputAction _inputAction;

    private void Awake()
    {
        // InputActionのコールバックの設定
        _inputAction.performed += OnPerformed;
    }

    private void OnDestroy()
    {
        // InputActionのコールバックの解除
        _inputAction.performed -= OnPerformed;

        // InputActionの破棄
        _inputAction.Dispose();
    }

    private void OnEnable()
    {
        // InputActionの有効化
        _inputAction.Enable();
    }

    private void OnDisable()
    {
        // InputActionの無効化
        _inputAction.Disable();
    }

    // InputActionのコールバック
    private void OnPerformed(InputAction.CallbackContext context)
    {
        Debug.Log("Performedが呼ばれた!");
    }
}

上記をFromInspectorExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチするとインスペクターよりActionを編集できるようになります。

実行結果は1つ目の例と一緒のため割愛します。

スクリプトの説明

1つ目の例とは異なり、フィールドをシリアライズ可能にして定義しています。

// インスペクターから設定可能にする
[SerializeField] private InputAction _inputAction;

これにより、インスペクターから専用UIでActionを設定できるようになります。

したがって、インスタンス生成処理をスクリプトから行う必要がなくなり、初期化処理はシンプルになります。

private void Awake()
{
    // InputActionのコールバックの設定
    _inputAction.performed += OnPerformed;
}

InputActionReferenceクラスを使う

ラッパーやPlayer Inputを使わずともInput Action Asset内のActionにアクセスする方法もあります。

このようなActionへの参照はInputActionReferenceクラスを用いると簡単に実現可能です。

参考:Class InputActionReference | Input System | 1.5.1

主な特徴
  • Input Action Assetの設定情報を参照する
  • 内部的にはInput Action Asset(ScriptableObject)をスクリプトから参照する
  • 有効化・無効化の管理はスクリプト側が責任を持って行う

サンプルスクリプト

以下、InputActionReferenceクラスを通じてInput Action Asset内部のActionの入力を受け取ってログ出力する例です。

InputActionReferenceExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class InputActionReferenceExample : MonoBehaviour
{
    // InputActionAsset内のActionへの参照
    [SerializeField] private InputActionReference _actionRef;

    private InputAction _action;

    private void Awake()
    {
        _action = _actionRef.action;
        if (_action == null) return;

        // InputActionのコールバックの設定
        _action.performed += OnFire;
    }

    private void OnDestroy()
    {
        if (_action == null) return;

        // InputActionのコールバックの解除
        _action.performed -= OnFire;
    }

    private void OnEnable()
    {
        // InputActionの有効化
        _action?.Enable();
    }

    private void OnDisable()
    {
        // InputActionの無効化
        _action?.Disable();
    }

    private void OnFire(InputAction.CallbackContext context)
    {
        print($"{context.action.name} Actionが呼ばれた!");
    }
}

上記をInputActionReferenceExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチし、インスペクターよりActionを指定してください。

実行結果は前述の例と一緒のため割愛します。

スクリプトの説明

次のようにInputActionReferenceフィールドをシリアライズすると、インスペクターからInput Action Asset内のActionを指定できるようになります。

// InputActionAsset内のActionへの参照
[SerializeField] private InputActionReference _actionRef;

インスペクターから指定された(シリアライズされた)Actionは、InputActionReference.actionプロパティから取得できます。

private InputAction _action;

・・・(中略)・・・

_action = _actionRef.action;

これ以降のActionの有効化やコールバック登録の流れは、前述の例と一緒です。

メモ

InputActionReference.assetプロパティからInputActionAssetインスタンスそのものにアクセスすることも可能です。

InputActionAsset asset = _actionRef.asset;

参考:Class InputActionReference | Input System | 1.5.1

InputActionProperty構造体を使う

InputActionクラスによるAction情報の直接指定、およびInputActionReferenceクラスによるInput Action AssetのActionへの参照を場面に応じて切り替えたい場合、InputActionProperty構造体を使うと簡単です。

参考:Struct InputActionProperty | Input System | 1.5.1

詳細の使い方は以下記事で解説しています。

主な特徴
  • 柔軟性が高い
  • 内部的にInputActionとInputActionReferenceのどちらを使うか切り替えられる
  • それ以外はそれぞれ切り替えた側のクラスの使い方と一緒

サンプルスクリプト

以下、InputActionProperty構造体経由でActionを使う例です。

InputActionPropertyExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class InputActionPropertyExample : MonoBehaviour
{
    // InputActionまたはInputActionReferenceを設定する
    [SerializeField] private InputActionProperty _actionProperty;

    private InputAction _action;

    private void Awake()
    {
        _action = _actionProperty.action;
        if (_action == null) return;

        // InputActionのコールバックの設定
        _action.performed += OnFire;
    }

    private void OnDestroy()
    {
        if (_action == null) return;

        // InputActionのコールバックの解除
        _action.performed -= OnFire;
    }

    private void OnEnable()
    {
        // InputActionの有効化
        _action?.Enable();
    }

    private void OnDisable()
    {
        // InputActionの無効化
        _action?.Disable();
    }

    private void OnFire(InputAction.CallbackContext context)
    {
        print($"{context.action.name} Actionが呼ばれた!");
    }
}

上記をInputActionPropertyExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチします。

すると、次のような設定項目がインスペクター上に表示されます。

Use Referenceチェックを外すActionを直接設定(InputAction相当)チェックを入れるとInput Action AssetのActionを指定(InputActionReference相当)が可能になります。

実行結果は先ほどと一緒のため割愛します。

スクリプトの説明

フィールドを定義するところ以外は、InputActionReferenceの使い方と一緒です。

// InputActionまたはInputActionReferenceを設定する
[SerializeField] private InputActionProperty _actionProperty;

InputActionインスタンスの取得も同様です。

_action = _actionProperty.action;

さいごに

Input SystemのActionの入力をスクリプトから取得する代表的な方法を一通り記しました。

Input Action Assetのラッパークラスを生成したり、Player Inputを使用する方法が代表的でしょう。

これ以外にも、InputActionを直接使用したり、InputActionReferenceでActionを参照したり、この両方を選択可能なInputActionPropertyを使用するといった方法もあります。

特にPlayer Input経由で取得する方法は複数コントローラーを扱うローカルマルチの実装が楽になるメリットがあるので、場面によって使い分けるのも良いでしょう。

関連記事

参考サイト

スポンサーリンク