【Unity】Input Systemでスティック入力を受け取る

こじゃらこじゃら

ゲームパッドのスティック入力の受け取り方を教えて欲しいの。

このはこのは

縦横の2軸入力として得られるわ。具体的な使い方について見ていくね。

Input Systemでゲームパッドやジョイスティックなどのスティック入力を取得する方法の解説記事です。

スティック入力は横方向(x軸)縦方向(y軸)2軸の入力値として得られます。

// ゲームパッド(デバイス取得)
var gamepad = Gamepad.current;
if (gamepad == null) return;

// ゲームパッドの左右のスティックの入力値を取得
Vector2 leftStick = gamepad.leftStick.ReadValue();
Vector2 rightStick = gamepad.rightStick.ReadValue();

通常、スティックの2軸入力は、プログラム中ではVector2型として扱います。Input Actionでは、Gamepad配下のleftStickrightStickを設定すれば良いです。

スティック入力では、これ以外にも上下左右の入力や、x軸またはy軸方向のみの入力を取り出すことも可能です。

本記事では、Input Systemでスティック入力を受け取る方法について基本的な部分から解説していきます。後半では、上下左右やxy軸方向のみの入力受け取り応用についても触れます。

動作環境
  • Unity 2023.2.0f1
  • Input System 1.7.0

スポンサーリンク

前提条件

事前にInput Systemパッケージがインストールされ、有効化されているものとします。ここまでの手順が分からない方は、以下記事を参考にセットアップを済ませてください。

また、本記事のサンプルを実行するためには、予めゲームパッドが接続されている必要があります。

Gamepadクラスから2軸入力を受け取る

ゲームパッドの左右スティックの2軸入力を取得することを例にとって解説します。

Input Actionを使わずにゲームパッドのデバイスから直でスティック入力を受け取るには、Gamepadクラスを使用します。

public class Gamepad : InputDevice, IDualMotorRumble, IHaptics

参考:Class Gamepad | Input System | 1.7.0

接続されているゲームパッドのデバイスの実体は、Gamepad.currentプロパティから取得できます。

public static Gamepad current { get; }

そして、左スティック、右スティックの入力を受け取るためのControl(入力ソース)はそれぞれGamepad.leftStickGamepad.rightStickプロパティから取得できます。

public StickControl leftStick { get; protected set; }
public StickControl rightStick { get; protected set; }

両者はStickControlというControlの一種です。

入力値はStickControl.ReadValueメソッドで取得できます。

public Vector2 ReadValue()

参考:Class InputControl<TValue> | Input System | 1.7.0

メモ

ゲームパッド(Gamepadクラス)はデバイス、スティックのControl(StickControl)はControlと呼ばれる入力ソースを表します。

デバイスやControlなどの仕組みについて知りたい方は、以下記事をご覧ください。

サンプルスクリプト

以下、ゲームパッドの左右スティックの2軸入力値を取得してログ出力する例です。

StickExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class StickExample : MonoBehaviour
{
    private void Update()
    {
        // ゲームパッド(デバイス取得)
        var gamepad = Gamepad.current;
        if (gamepad == null) return;

        // ゲームパッドの左右のスティックの入力値を取得
        // 得られる結果はVector2型
        var leftStick = gamepad.leftStick.ReadValue();
        var rightStick = gamepad.rightStick.ReadValue();

        print($"leftStick: {leftStick}, rightStick: {rightStick}");
    }
}

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

実行結果

ゲームパッドを接続した状態でゲームを実行すると、左右スティックの入力値が毎フレームログ出力されます。

それぞれの軸の入力値が-1〜1の範囲で変化していることが確認できます。

Input Action経由で2軸入力を受け取る

Input Action経由でスティック入力を受け取るには、次のControlをBindingから指定すれば良いです。

Gamepad
  ├─leftStick
  └─rightStick
Joystick
  └─stick

本記事では、Input Action Assetにスティック入力を受け取るものとして解説していきます。

Input Action Assetでの設定

Input Action Assetで設定する場合は、対象のActionにBindingを追加し、PathスティックのControl(Control Path)を指定します。

キャラクターの移動など、スティック入力を毎フレーム扱いたい場合Action TypeValueを設定します。これは、入力値の変化があるたびに入力を受け取る設定です。 [1]

また、スティック入力はVector2型として扱うため、Control TypeVector 2に設定します。

以下、「Move」というActionを作成し、更にBindingにゲームパッドの左スティックを指定する例です。

Input Actionの基本的な使い方については、以下記事をご覧ください。

スクリプトから入力を受け取る

前述で設定したInput Action Assetの入力を受け取るためのスクリプトを実装します。

インスペクターなどからActionを指定する方法はいくつか存在しますが、ここではInputActionReferenceクラスのフィールドを[SerializeField]プロパティで設定するものとします。

以下、実装例です。

ActionExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class ActionExample : MonoBehaviour
{
    // InputActionAssetへの参照
    [SerializeField] private InputActionReference _moveAction;

    // コールバックの登録・解除
    private void Awake()
    {
        // 入力値が0以外の値に変化したときに呼び出されるコールバック
        _moveAction.action.performed += OnMove;
        
        // 入力値が0に戻ったときに呼び出されるコールバック
        _moveAction.action.canceled += OnMove;
    }

    private void OnDestroy()
    {
        _moveAction.action.performed -= OnMove;
        _moveAction.action.canceled -= OnMove;
    }

    // InputActionの有効化・無効化
    private void OnEnable() => _moveAction.action.Enable();
    private void OnDisable() => _moveAction.action.Disable();

    // コールバックの実装
    private void OnMove(InputAction.CallbackContext context)
    {
        // 2軸入力を受け取る
        var move = context.ReadValue<Vector2>();

        // 2軸入力の値を表示
        print($"move: {move}");
    }
}

上記をActionExample.csという名前でUnityプロジェクトに保存し、適当なゲームオブジェクトにアタッチし、インスペクターからActionを設定します。

例では「Move」という名前のActionを指定するものとします。

実行結果

スティックが動かされるたびに入力値のログが出力されるようになります。

動画の例では分かりやすさのため、スティックの入力状態をUIで可視化しています。

スクリプトの説明

Input Action Assetで定義されたActionを指定できるようにするため、InputActionReferenceフィールドを[SerializeField]属性指定で定義しています。

// InputActionAssetへの参照
[SerializeField] private InputActionReference _moveAction;

参考:Class InputActionReference | Input System | 1.7.0

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

実際のAction(InputActionクラスのインスタンス)はInputActionReference.actionプロパティから取得できます。

参考:Class InputActionReference | Input System | 1.7.0

例ではActionの入力をコールバック経由で受け取るため、次のコードでInputAction.performedInputAction.canceledコールバックの登録と解除を行なっています。

// コールバックの登録・解除
private void Awake()
{
    // 入力値が0以外の値に変化したときに呼び出されるコールバック
    _moveAction.action.performed += OnMove;
    
    // 入力値が0に戻ったときに呼び出されるコールバック
    _moveAction.action.canceled += OnMove;
}

private void OnDestroy()
{
    _moveAction.action.performed -= OnMove;
    _moveAction.action.canceled -= OnMove;
}

canceledコールバックもチェックする理由は、スティックがニュートラルに戻って(0, 0)が返る際はperformedではなくcanceledが発火するためです。

参考:Class InputAction | Input System | 1.7.0

参考:Interactions | Input System | 1.7.0

また、上記Actionは定義しただけでは入力を受け取れません。受け取るためにはInputAction.Enableメソッドを呼ぶ必要があります。

// InputActionの有効化・無効化
private void OnEnable() => _moveAction.action.Enable();
private void OnDisable() => _moveAction.action.Disable();

参考:Class InputAction | Input System | 1.7.0

コールバック内では、InputAction.CallbackContext型の引数に対してReadValue<Vector2>メソッドを実行することで、スティックの2軸入力値を受け取れます。

// コールバックの実装
private void OnMove(InputAction.CallbackContext context)
{
    // 2軸入力を受け取る
    var move = context.ReadValue<Vector2>();

    // 2軸入力の値を表示
    print($"move: {move}");
}

参考:Struct InputAction.CallbackContext | Input System | 1.7.0

Tips

コールバックではなく、Updateイベント内でスティック入力を受け取りたい場合InputAction.ReadValue<Vector2>メソッドで直接受け取れます。

UpdateExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class UpdateExample : MonoBehaviour
{
    // InputActionAssetへの参照
    [SerializeField] private InputActionReference _moveAction;

    // InputActionの有効化・無効化
    private void OnEnable() => _moveAction.action.Enable();
    private void OnDisable() => _moveAction.action.Disable();

    private void Update()
    {
        var move = _moveAction.action.ReadValue<Vector2>();
        print($"move: {move}");
    }
}

参考:Struct InputAction.CallbackContext | Input System | 1.7.0

特定方向のみの入力を受け取る

スティックの2軸入力ではなく、xy軸方向のみの入力や上下左右のみの入力を取得したい場合、スティックの子Controlを指定すれば良いです。

  • <StickControl>/x – x軸方向の入力
  • <StickControl>/y – y軸方向の入力
  • <StickControl>/up – 上入力(下入力では0)
  • <StickControl>/down – 下入力(上入力では0)
  • <StickControl>/left – 左入力(右入力では0)
  • <StickControl>/right – 右入力(左入力では0)

Input Actionを使わず上記にアクセスするには、次のようにStickControlの各種プロパティを参照します。

// ゲームパッド(デバイス取得)
var gamepad = Gamepad.current;
if (gamepad == null) return;
    
// ゲームパッドの左右のスティックの入力値を取得
float x = gamepad.leftStick.x.ReadValue();
float y = gamepad.leftStick.y.ReadValue();
float up = gamepad.rightStick.up.ReadValue();
float down = gamepad.rightStick.down.ReadValue();
float left = gamepad.rightStick.left.ReadValue();
float right = gamepad.rightStick.right.ReadValue();

参考:Class StickControl | Input System | 1.7.0

参考:Class Vector2Control | Input System | 1.7.0

Input Action経由で受け取る場合は、次のようなControl PathをBindingに指定すれば良いです。

  • <Gamepad>/leftStick/x – 左スティックのx軸方向入力
  • <Gamepad>/leftStick/y – 左スティックのy軸方向入力
  • <Gamepad>/leftStick/up – 左スティックの上入力
  • <Gamepad>/leftStick/down – 左スティックの下入力
  • <Gamepad>/leftStick/left – 左スティックの左入力
  • <Gamepad>/leftStick/right – 左スティックの右入力

右スティックの場合は「leftStick」の部分が「rightStick」になります。

Gamepadクラスから直接入力を受け取る例

以下、Updateイベントでゲームパッドの左スティックのxy軸方向入力および上下左右入力を個別で受け取り、ログ出力する例です。

ChildStickControlExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class ChildStickControlExample : MonoBehaviour
{
    private void Update()
    {
        // ゲームパッド(デバイス取得)
        var gamepad = Gamepad.current;
        if (gamepad == null) return;
            
        // ゲームパッドの左右のスティックの入力値を取得
        var x = gamepad.leftStick.x.ReadValue();
        var y = gamepad.leftStick.y.ReadValue();
        var up = gamepad.rightStick.up.ReadValue();
        var down = gamepad.rightStick.down.ReadValue();
        var left = gamepad.rightStick.left.ReadValue();
        var right = gamepad.rightStick.right.ReadValue();
        
        // 全ての入力をログ出力
        print($"x: {x}, y: {y}, up: {up}, down: {down}, left: {left}, right: {right}");
    }
}

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

実行結果

xy軸方向と上下左右の入力が独立して得られることが確認できました。

動画では、それぞれの入力をUIで可視化しています。

Input Action経由で入力を受け取る例

Input Actionで受け取る場合は、ActionのControl TypeAxisに設定します。これは、プログラム中ではfloat型として扱われるためです。

Control TypeにAxisを指定した状態でBindingのControlを選択すると、候補としてxy軸方向や上下左右のControlが表示されます。

Left StickやRight Stickなどの2軸入力は非アクティブになって選択不可になり、代わりにボタン系のControl(1軸入力)が選択可能になっていることが確認できます。

入力を受け取るスクリプトの例

以下、スティックの子のActionの入力を受け取る例です。

ChildActionExample.cs
using UnityEngine;
using UnityEngine.InputSystem;

public class ChildActionExample : MonoBehaviour
{
    // InputActionAssetへの参照
    [SerializeField] private InputActionReference _stickChildAction;

    // コールバックの登録・解除
    private void Awake()
    {
        _stickChildAction.action.performed += OnMove;
        _stickChildAction.action.canceled += OnMove;
    }

    private void OnDestroy()
    {
        _stickChildAction.action.performed -= OnMove;
        _stickChildAction.action.canceled -= OnMove;
    }

    // InputActionの有効化・無効化
    private void OnEnable() => _stickChildAction.action.Enable();
    private void OnDisable() => _stickChildAction.action.Disable();

    // コールバックの実装
    private void OnMove(InputAction.CallbackContext context)
    {
        // スティックの子Controlの入力を受け取る
        var childValue = context.ReadValue<float>();

        // 値をログ出力
        print($"childValue: {childValue}");
    }
}

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

例では、x軸方向の左スティック入力のActionを指定するものとします。

実行結果

指定したスティック方向の入力のみが取得されていることが確認できました。

スクリプトの説明

主な変更点はコールバック処理の以下部分です。

// コールバックの実装
private void OnMove(InputAction.CallbackContext context)
{
    // スティックの子Controlの入力を受け取る
    var childValue = context.ReadValue<float>();

    // 値をログ出力
    print($"childValue: {childValue}");
}

ReadValueメソッドの型引数がfloat型になっている点以外は一緒です。

応用例の紹介

ここでは、スティック入力を受け取る際に遭遇すると思われる事象への対処方法について軽く紹介します。詳細は別記事で解説しています。

ドリフト現象への対応

ゲームパッドの種類やスティックの劣化などによって、スティックを倒していないのに僅かな入力値が入ってしまうことがあります。

このようなドリフト現象に対応するには、ある一定以下の大きさの入力を無しとみなす処理を施す必要があります。

Input Actionを使用して入力値を受け取る場合は、Stick DeadzoneAxis DeadzoneといったProcessorを使うと簡単です。スティックの2軸入力、特定方向の1軸入力両方に対応しています。

詳細な使い方は以下記事をご覧ください。

上下左右を十字キーのようなボタンとして扱う

スティックの上下左右入力は前述の通り独立した入力として取得できることを解説しました。

これらの入力はボタンとして扱うことが可能です。これにより、スティックが上下左右に倒された瞬間にコールバックを受け取って何か処理するといったことが可能です。

この辺の実現方法は以下記事で解説しています。

十字キーやキーボードのWASDキー、矢印キーなどをスティックのように扱う

逆に4方向のボタンをスティックのような2軸入力として扱いたい場合は、Composite Bindingの2D Vectorを使うと簡単です。

これにより、入力を受け取るスクリプトはスティック同様にVector2型として受け取ることができ、スティック入力を受け取るのと全く同じコードにできます。

Composite Bindingの基本的な解説は以下記事をご覧ください。

また、2D Vector Compositeのより詳細な挙動は以下記事で解説しています。

マウス座標などの2軸入力との違い

スティック入力とマウス座標はどちらも同じVector2型として入力を受け取ります。

どちらもVector2ControlというControlという点で共通ですが、スティックの方はVector2Controlから派生したStickControlとなっており、StickControlが上下左右入力を個別で取得するための子Controlが追加されている点で異なります。

参考:Class Vector2Control | Input System | 1.7.0

参考:Class StickControl | Input System | 1.7.0

さいごに

スティック入力は、ゲームパッドやジョイスティックの子のControlから2軸入力(Vector2型)として取得できます。

更に、xy軸方向や上下左右の独立した一部の入力のみを取得できます。これはスティックのControl(StickControl)の子階層のControlとして実装されています。

これによって、スティックの方向入力をボタンのように扱えるといった特徴があります。

関連記事

参考サイト

スポンサーリンク