ジョイスティックで操作する (入力システムパッケージ版)

ページ更新日 :
ページ作成日 :

検証環境

Windows
  • Windows 11
Unity エディター
  • 2020.3.25f1
入力システムパッケージ
  • 1.2.0

この Tips の前提設定

この Tips の説明の前提として以下の設定を事前に行っています。

XInput と DirectInput について

Windows 限定の話になりますが、ゲームコントローラーの接続形式として「DirectInput」と「XInput」というものがあります。 ここでの「ジョイスティック」は「DirectInput」に該当します。

今回のプログラムでは Joystick クラスを扱っていますがこれは「DirectInput」に対応したコントローラーしか扱うことができません。 「XInput」に対応したコントローラーを使うには別の Gamepad クラスを使う必要があります。

「DirectInput」は古くからある接続形式でボタンの定義などが割とあいまいになっており特殊な形状のコントローラーなどを扱うことができます。 しかし最近は「XInput」が主流になっていることもあり「DirectInput」に対応していないコントローラーも増えてきています。 「DirectInput」はボタンの定義が「1」「2」「3」という風になっているのでゲーム制作者はゲームとコントローラーのボタン対応を適切に設定できるように作らなければなりません。

「XInput」は「DirectInput」の次世代として定義されたもので A ボタンや B ボタン、トリガー、スティックなどあらかじめ定義されています。 そのためコントローラーの形状はほぼ決まったものしか使えませんが、 ボタンなどの定義がきちんと決まっているのでゲーム制作者はボタンの配置などを気にせずにコントローラーに合ったゲームを作ることができます。 最近のゲームコントローラーでは「XInput」のみに対応しているものも増えてきています。

ジョイスティックについて

前述のとおりボタンの定義などがないため、実際に使用するには各ボタンの情報を調べながらゲームの操作を割り当てるというような面倒な処理を入れる必要があります。 DirectInput は古いので新しいゲームで採用する必要はないと思いますが、どうしても対応させたい場合はプログラムが複雑になる可能性があることに注意してください。

今回のサンプルも基本的に列挙したボタンの情報を取得するだけにとどめています。 実際には name などを確認して操作を割り当てる必要があります。

ジョイスティックの情報取得

ジョイスティックの各ボタンの情報を表示させるためのテキストオブジェクトを配置します。

スクリプトを作成します。名前は任意ですがここでは JoystickInfo とします。

スクリプトは以下のようにします。 コントローラーの種類によって得られる情報がまちまちであり、 各コントローラーに合わせてプログラムを入れるのは非常に困難なので全てのボタンを列挙してボタンの種類と押下情報を取得するだけにしています。

using System.Text;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.UI;

public class JoystickInfo : MonoBehaviour
{
  /// <summary>情報を表示させるテキストオブジェクト。</summary>
  [SerializeField] private Text TextObject;

  StringBuilder Builder = new StringBuilder();

  // 更新はフレームごとに1回呼び出されます
  void Update()
  {
    if (TextObject == null)
    {
      Debug.Log($"{nameof(TextObject)} が null です。");
      return;
    }

    // 1つ目のジョイスティックの情報を取得
    var joystick = Joystick.current;
    if (joystick == null)
    {
      Debug.Log("ジョイスティックがありません。");
      TextObject.text = "";
      return;
    }

    Builder.Clear();

    // ジョイスティックの情報取得
    Builder.AppendLine($"deviceId:{joystick.deviceId}");
    Builder.AppendLine($"name:{joystick.name}");
    Builder.AppendLine($"displayName:{joystick.displayName}");

    // ジョイスティックにはボタンの大部分が定義されていないので
    // 基本的には allControls で列挙して入力の種類と値を確認していく
    foreach (var key in joystick.allControls)
    {
      if (key is StickControl stick)
      {
        if (stick.up.isPressed) Builder.AppendLine($"{key.name} Up");
        if (stick.down.isPressed) Builder.AppendLine($"{key.name} Down");
        if (stick.left.isPressed) Builder.AppendLine($"{key.name} Left");
        if (stick.right.isPressed) Builder.AppendLine($"{key.name} Right");

        var value = stick.ReadValue();
        if (value.magnitude > 0f)
        {
          Builder.AppendLine($"{key.name}:{value.normalized * value.magnitude}");
        }
      }
      else if (key is Vector2Control vec2)
      {
        var value = vec2.ReadValue();
        if (value.magnitude > 0f)
        {
          Builder.AppendLine($"{key.name}:{value.normalized * value.magnitude}");
        }
        if (vec2.x.ReadValue() != 0f)
        {
          Builder.AppendLine($"{key.name}.x:{vec2.x.ReadValue()}");
        }
        if (vec2.y.ReadValue() != 0f)
        {
          Builder.AppendLine($"{key.name}.x:{vec2.y.ReadValue()}");
        }
      }
      else if (key is ButtonControl button)
      {
        if (button.isPressed) Builder.AppendLine($"{key.name} isPress");
        var value = button.ReadValue();
        if (value != 0f)
        {
          Builder.AppendLine($"{key.name}:{value}");
        }
      }
      else if (key is AxisControl axis)
      {
        if (axis.IsPressed()) Builder.AppendLine($"{key.name} isPress");

        var value = axis.ReadValue();
        if (value != 0f)
        {
          Builder.AppendLine($"{key.name}:{value}");
        }
      }
      else
      {
        Builder.AppendLine($"Type={key.GetType()}");
      }
    }

    // 押しているボタン一覧をテキストで表示
    TextObject.text = Builder.ToString();
  }
}

接続されているジョイスティックの情報は Joystick.current で取得できます。 あとは Joystick クラスから各情報を取得する形になります。

Joystick クラスを見ると分かりますが、一応 sticktrigger などの定義はありますが A ボタンやスタートボタンなどの定義はありません。 そのため、allControls プロパティを使用してすべてのボタン、スティックを列挙してボタンの種類と押下情報を調べる必要があります。

スクリプトを保存したら EventSystem にアタッチして情報表示用のテキストオブジェクトをセットします。

ゲームを動かして DirectInput に対応したコントローラーで動かしてみてください。各情報を取得することができます。

コントローラーにもよりますが、スティックや方向キーなどを押したときに意図したような形で値が取得できない場合があります。 このようにボタンが定義されていないため各ボタンの正確な押下情報の取得が難しく、 さらにコントローラーによってボタンの配置が異なっていたりとジョイスティックに完全に対応するのはなかなか困難であることが分かります。

もしゲームをジョイスティックに対応させるならキーコンフィグのような設定画面を用意して各ユーザーが持っているコントローラーのボタン配置をカスタマイズできるようにするのが望ましいかと思います。

複数のジョイスティックの情報を取得する方法

接続されている全てのジョイスティックの情報は Joystick.all で取得することができます。 ReadOnlyArray で定義されているので foreach などで列挙することができます。

各値は Joystick クラスなので前述のプログラムと同様にジョイスティックの情報を取得することができます。