【Unity】Input Systemでゲームパッドを振動させる

Input Systemからゲームパッドを振動させることって出来るの?

出来るわ。振動させるAPIが用意されているので簡単だわ。

Input System経由でゲームパッドを振動させる方法の解説です。

振動自体は以下のようなコードで簡単に指定できます。

// 左モーターを50%の速さで回転
// 右モーターを停止させる
Gamepad.current?.SetMotorSpeeds(0.5f, 0.0f);

例ではゲームパッドのデバイスを直接参照していますが、この辺の処理は抽象化することも可能です。

本記事では、ゲームパッドを振動させる方法とその注意点について解説していきます。

動作環境
  • Unity 2021.3.0f1
  • Input System 1.3.0

スポンサーリンク

前提条件

Input Systemがインストールされ、有効化されているものとします。

ここまでの手順が分からない方は、以下記事をご覧ください。

【Unity】Input Systemの使い方入門
Unity公式の新しい入力システムパッケージInput Systemの入門者向け記事です。 本記事では、Input Systemパッケージのインストール方法から、最低限使えるようにするところまでを解説します。 スポンサー ...

ゲームパッドを振動させる最低限のコード

ゲームパッドの左右モーターをそれぞれ1秒ずつ振動させるサンプルスクリプトです。

using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;

public class GamepadRumbleExample : MonoBehaviour
{
    private IEnumerator Start()
    {
        var gamepad = Gamepad.current;
        if (gamepad == null)
        {
            Debug.Log("ゲームパッド未接続");
            yield break;
        }

        Debug.Log("左モーター振動");
        gamepad.SetMotorSpeeds(1.0f, 0.0f);
        yield return new WaitForSeconds(1.0f);

        Debug.Log("右モーター振動");
        gamepad.SetMotorSpeeds(0.0f, 1.0f);
        yield return new WaitForSeconds(1.0f);

        Debug.Log("モーター停止");
        gamepad.SetMotorSpeeds(0.0f, 0.0f);
    }
}

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

ゲームパッドを振動させるためには、予めゲームパッドを接続した状態にしてください。

接続された状態でゲームを実行すると、左右のモーターがそれぞれ1秒間だけ振動して停止します。

本スクリプトは以下環境で動作確認しています。

テスト環境
  • Windows10 Pro
  • DUALSHOCK 4(PS4コントローラ)

スクリプトの解説

以下のコードでゲームパッドの左右モーターを振動させています。

gamepad.SetMotorSpeeds(1.0f, 0.0f);

第1引数左モーター(低周波)の回転数第2引数右モーター(高周波)の回転数0~1の範囲で指定します。

0を指定するとモーターの回転が停止、1を指定すると最大出力でモーターが回転します。

参考:Class Gamepad | Input System | 1.3.0

注意

SetMotorSpeeds()メソッドでコントローラのモーターを動かすと止まらずずっと振動し続けます。ゲームを停止しても動き続けますので、必ず回転数0を指定して振動を止める処理を入れてください。

コントローラの振動を一括で停止・再開する

以下メソッドを実行すると、Input Systemで管理しているコントローラの振動を一括で停止・再開できます。

// 振動を一時停止
InputSystem.PauseHaptics();

// 振動を再開
InputSystem.ResumeHaptics();

// 振動を停止し、パラメータリセット
InputSystem.ResetHaptics();

参考:Gamepad Support | Input System | 1.3.0

InputSystem.PauseHaptics()メソッドは、コントローラの振動を一時停止するメソッドです。回転数などの設定値は保持されます。

InputSystem.ResumeHaptics()メソッドは、コントローラの振動を再開するメソッドです。保持された設定値で振動が再開されます。

InputSystem.ResetHaptics()メソッドは、コントローラの振動を停止し、設定値を初期値にリセットします。

サンプルスクリプト

上記メソッドの挙動を確かめるためのサンプルスクリプトを示します。

PauseResumeExample.cs
using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;

public class PauseResumeExample : MonoBehaviour
{
    private IEnumerator Start()
    {
        var gamepad = Gamepad.current;
        if (gamepad == null)
        {
            Debug.Log("ゲームパッド未接続");
            yield break;
        }
        
        var wait = new WaitForSeconds(1.0f);

        Debug.Log("左モーター振動");
        gamepad.SetMotorSpeeds(1.0f, 0.0f);
        yield return wait;

        Debug.Log("振動を一時停止");
        InputSystem.PauseHaptics();
        yield return wait;
        
        Debug.Log("振動を再開");
        InputSystem.ResumeHaptics();
        yield return wait;

        Debug.Log("振動を停止し、パラメータリセット");
        InputSystem.ResetHaptics();
        yield return wait;

        Debug.Log("振動を再開(初期値なので振動しない)");
        InputSystem.ResumeHaptics();
        yield return wait;
        
        Debug.Log("振動を停止");
        InputSystem.ResetHaptics();
    }
}

実行結果

ゲームパッドが接続された状態で起動すると、ゲームパッドの左モーターが2回だけ振動するようになります。

InputSystem.PauseHaptics()→InputSystem.ResumeHaptics()の順に呼び出したタイミングでは振動が再開されるのに対し、InputSystem.ResetHaptics()→InputSystem.ResumeHaptics()の順に呼び出した際は振動しません。

これは、InputSystem.ResetHaptics()呼び出しの時点で回転数が初期値(=0)に戻っているためです。

Player Input経由で抽象化されたコントローラを振動させる

Player Inputコンポーネントを経由して、抽象化されたコントローラを振動させる方法の紹介です。Gamepadクラスに直接アクセスすることなく前述の振動を実現できます。

サンプルスクリプト

Player Inputから振動可能なコントローラを取得し、振動させるサンプルです。

PlayerInputRumbleExample.cs
using System.Collections;
using System.Linq;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Haptics;

[RequireComponent(typeof(PlayerInput))]
public class PlayerInputRumbleExample : MonoBehaviour
{
    private IEnumerator Start()
    {
        // PlayerInputインスタンスを取得
        var playerInput = GetComponent<PlayerInput>();
        
        // PlayerInputから振動可能なデバイス取得
        // playerInput.devicesは現在選択されているスキームのデバイス一覧であることに注意
        if (playerInput.devices.FirstOrDefault(x => x is IDualMotorRumble) is not IDualMotorRumble gamepad)
        {
            Debug.Log("デバイス未接続");
            yield break;
        }

        // 振動
        Debug.Log("コントローラ振動開始");

        gamepad.SetMotorSpeeds(1.0f, 0.0f);
        yield return new WaitForSeconds(1.0f);

        gamepad.SetMotorSpeeds(0.0f, 1.0f);
        yield return new WaitForSeconds(1.0f);

        gamepad.SetMotorSpeeds(0.0f, 0.0f);

        Debug.Log("コントローラ振動停止");
    }
}

PlayerInputRumbleExample.csという名前で保存し、PlayerInputコンポーネントがアタッチされているゲームオブジェクトにアタッチすると機能するようになります。

サンプルではゲーム起動時に選択中スキームのデバイスを取得するため、以下のようにPlayer InputのDefault SchemeをGamepadにしておく必要があります。

Player Inputの使い方の詳細を知りたい方は、以下記事をご覧ください。

【Unity】Player InputでInput Systemの入力を取得する
Input Systemで提供されているPlayer Inputの使い方の解説です。 Player Inputは、Input Systemの入力を仲介するコンポーネントで、Input Actionの入力をスクリプト側に通 ...

実行結果

ゲームパッドを接続した状態でゲームを実行すると、左右モーターを振動させ、次のようなログを出力します。

スクリプトの解説

次の部分で振動可能なデバイスを取得しています。

if (playerInput.devices.FirstOrDefault(x => x is IDualMotorRumble) is not IDualMotorRumble gamepad)

SetMotorSpeeds()メソッドは、IDualMotorRumbleインタフェースのメソッドを実装したものなので、デバイスをIDualMotorRumbleにキャストしています。

キャストできるデバイスがあれば、振動させる処理に移行します。

gamepad.SetMotorSpeeds(1.0f, 0.0f);
yield return new WaitForSeconds(1.0f);

gamepad.SetMotorSpeeds(0.0f, 1.0f);
yield return new WaitForSeconds(1.0f);

gamepad.SetMotorSpeeds(0.0f, 0.0f);

さいごに

Input System経由でゲームパッドを振動させる方法は、特定のAPIを呼び出すだけなので、比較的簡単に実現可能です。

SDKをインストールしたり、ネイティブコードを書いたりといった面倒な実装をする必要がない点が魅力です。

参考サイト

スポンサーリンク