目次
Input Systemとは
Input Systemは、ゲームパッド・キーボード・マウス・センサー等の各種入力デバイスからの入力を汎用的・統合的に取り扱うために導入されたUnityの新しい入力システムです。現在、Package Managerから追加でインストールするパッケージとして提供されており、2020年4月にversion 1.0.0として正式版になりました。
Input SystemはUnityが対応しているすべてのプラットフォームをサポートします。これまでのUnityの入力システムを置き換えるもので、今後標準の入力システムとなる見込みですので、今のうちに慣れ親しんでおくとよいでしょう。
これまでの入力システムではなぜ駄目なのか
Unityを使用している開発者であれば、Project SettingsのInput Managerでゲームパッドやキーボードの操作をアサインして、Input.GetAxisやInput.GetButtonDown等で状態を取得するこれまでの方法にすでに慣れ親しんでいることと思います。なぜここに来て新しい入力システムが必要なのでしょうか?
Input ManagerはUnityの初期のバージョンからずっと使われてきたシステムです。その間、ゲームで使用される入力デバイスにはさまざまな変化がありました。最も大きな変化はスマートフォンやタブレットの登場、すなわちタッチデバイスの登場でしょう。これまでの入力システムではタッチ入力がカバーされておらず、例えばXboxコントローラーとiPhoneのバーチャルスティックの操作を同じように取り扱うことができません。
また、ゲームパッドの入力が標準化されていない問題もあります。Input ManagerではAxis の何番、Buttonの何番といった素朴な方法で入力を割り当てていましたが、「XboxコントローラーのBボタンは何番?」「PS4コントローラーのL2トリガーは何番だっけ?」といったことがしばしば発生します。異なるコントローラーを使うたびにマッピングを調べるのは大変ですし、軸とボタンのマッピングのメモを手元に用意している開発者も多いのではないでしょうか。
さらに、ゲームの実行中にゲームパッドを抜いて別のゲームパッドに差し替えたいということもあると思います。
新しいInput Systemは、これまでのUnityの入力システムではカバーできていなかったこうした課題をまとめて解決しようとするものです。
どんなコントローラーを扱えるのか
新しいInput Systemはあらゆる入力デバイスからの入力を統一的に扱えることを目標としています。キーボード、マウス、タッチ入力はもちろんとして、ゲームパッドでは
- Xboxコントローラー等XInput方式のゲームパッド
- PS4コントローラー(デュアルショック)
- SwitchのJoy-Con(ビルドプラットフォームをNintendo Switchにした場合)
を共通して使えるようになっています。HIDのジョイスティック(「DirectInput方式」として販売されているゲームパッド等)も使用できます。
また、スマートフォンのジャイロ・加速度等のセンサーにも対応しています。その他、Unityで使用する可能性のあるさまざまな入力デバイスに、開発者が特別なことをしなくても対応できるようにしようとしており、今後その数は増えていく見込みです。

Input Systemをインストールする
それでは早速Input Systemを使ってみましょう。この記事ではUnity 2021.1、Input System version 1.0.2を使っていきます。新規プロジェクトを作成してください。
Package Managerを開き、左上のPackagesをUnity Registryに変更してInput Systemを探し、インストールします。
ダイアログが表示されて、新しいInput System用のネイティブバックエンドを有効にするかを聞いてきます。YesをクリックするとUnityが再起動します。
新しいInput System用のバックエンドに切り替えると、従来のUnityEngine.InputのAPIは使用できなくなりますので注意してください(使用しようとするとInvalidOperationExceptionが発生します)。
バックエンドの設定はProject SettingsのPlayer > Other Settings > Active Input Handlingに保存されており、後から変更できます。Bothに設定するとInput Systemと旧UnityEngine.Inputの両方のAPIが使用できるようになります。
とりあえずゲームパッドの状態を取得する
さて、まずはInput Systemを最も簡単な方法で使ってみましょう。
現在接続されているゲームパッドはGamepad.currentで参照できます。ゲームパッドが接続されていないときはnullになるため、参照する際はnullチェックが必要です。
試しに空のGameObjectに下記スクリプトをアタッチして、USBでXboxコントローラー(XInput対応ゲームパッド)もしくはPS4コントローラーを接続して再生してみてください。
using UnityEngine; using UnityEngine.InputSystem; public class GamepadExample : MonoBehaviour { void Update() { // ゲームパッドが接続されていないとnullになる。 if (Gamepad.current == null) return; if (Gamepad.current.buttonNorth.wasPressedThisFrame) { Debug.Log("Button Northが押された!"); } if (Gamepad.current.buttonSouth.wasReleasedThisFrame) { Debug.Log("Button Southが離された!"); } } void OnGUI() { if (Gamepad.current == null) return; GUILayout.Label($"leftStick: {Gamepad.current.leftStick.ReadValue()}"); GUILayout.Label($"buttonNorth: {Gamepad.current.buttonNorth.isPressed}"); GUILayout.Label($"buttonSouth: {Gamepad.current.buttonSouth.isPressed}"); GUILayout.Label($"buttonEast: {Gamepad.current.buttonEast.isPressed}"); GUILayout.Label($"buttonWest: {Gamepad.current.buttonWest.isPressed}"); GUILayout.Label($"leftShoulder: {Gamepad.current.leftShoulder.ReadValue()}"); GUILayout.Label($"leftTrigger: {Gamepad.current.leftTrigger.ReadValue()}"); GUILayout.Label($"rightShoulder: {Gamepad.current.rightShoulder.ReadValue()}"); GUILayout.Label($"rightTrigger: {Gamepad.current.rightTrigger.ReadValue()}"); } } |
Gamepad.currentからゲームパッドの一通りの状態を取得できることが分かると思います。例えば、isPressedでボタンが現在押されているかどうか、wasPressedThisFrameで現在のフレームに押し下げられたかどうかを取得できます。
最も特徴的なのは、buttonNorth、buttonSouth、buttonEast、buttonWestの東西南北の4つでしょう。これらによって、標準的なゲームパッドで十字にレイアウトされている4つのボタンの状態を取得できます。たとえばbuttonSouthは、XboxコントローラーのAボタン、PS4コントローラーの×ボタンにそれぞれ対応しています。
ゲームパッドが4つの十字レイアウトのボタンを持っているとは限らないのでは、という疑問も出てくると思うのですが、現在の主要なゲームプラットフォームすべてで十字レイアウトのボタンが採用されているため大丈夫という判断なのだろうと思います。
ゲームパッドを抜き差ししても認識が継続することを確認してください。さらには、XboxコントローラーからPS4コントローラーに差し替えても上記のコードのままで使用できます。ここまでの説明でも、すでに新Input Systemを使いたくなったという方も多いのではないでしょうか。
キーボードとマウスの状態を取得する
同様に、キーボードの状態はKeyboard.currentで取得できます。例えば、いずれかのキーが押されたかどうかはKeyboard.current.anyKey.wasUpdatedThisFrameで取得できます。マウスの状態はMouse.currentで取得できます。
Gamepadクラスで対応しているゲームパッド以外のジョイスティック(DirectInput方式の一般的なHIDゲームパッド等)の状態はJoystick.currentで取得します。Joystickクラスで認識するジョイスティックについては、旧Input Manager同様、軸とボタンの割り当てを確認する必要がありますので注意してください。
スマートフォンのタッチ入力その他の入力については後の項目で例示します。ここまでが基本的なInput Systemでの入力の取得方法です。
Actionを使う
さて、ここからはInput Systemのより高度な使い方に進みます。この新システムが本当の威力を発揮するところです。
Input Systemでは、Actionという抽象的な操作に各種コントローラーの入力をバインドしてアプリケーション中から取り扱うことできます。
空のGameObjectを作成して、Player Inputコンポーネントをアタッチしてください。Create Actions…をクリックすると.inputactionsという拡張子のファイルを作成しますので、試しにTestInputActionsという名前をつけて保存します。作成したTestInputActionsをPlayer InputコンポーネントのActionsにアサインしてください。
ProjectウィンドウのTestInputActionsファイルをダブルクリックするとActionを設定するウィンドウが開きます。初期状態で以下のような設定が入っています。ここでは分かりやすくするためにActionsのツリーを展開しています。
左の列から順番に、まずPlayerとUIというAction Maps(ゲーム中の操作モードのようなものだと考えてください)があり、PlayerアクションマップにはMove、Look、Fireの3つのアクションが定義されています。そして、それぞれのアクションについてゲームパッドやキーボード、マウス等のデバイスの実際の操作がバインドされています。
初期設定では、「移動する(Move)」という抽象的な操作に、ゲームパッドの左スティック、キーボードの矢印キー、FPS等でおなじみのWASDキー等といった具体的なデバイスの操作が関連づけられています。開発者はこうしたバインドの設定をした上で、ただMoveというアクションについて移動処理を行えばいいわけです。
アクションをそれぞれクリックで選択して、右のプロパティ欄でどのように定義されているか確認してください。MoveとLookはVector2の値のアクションとして、FireはButtonのアクションとして設定されていることが分かります。
さらに、コントロールスキームという概念があり、入力デバイスの切り替えをサポートしています。左上のAll Control Schemesと表示されているドロップダウンメニューをGamepadやKeyboardMouseに変更すると、その入力デバイスのみのバインドが表示されます。
Actionにバインドを追加する
ここで、ぜひ知っておいてほしい強力な機能をひとつ紹介します。
初期設定を見ると、Fireというアクションにはゲームパッドの右トリガーがバインドされているようです(Right Trigger [Gamepad])。一人称シューティングにはともかく一般的なゲームには向かないアサインに思えます。XboxコントローラーのAボタンでもFireできるようにしてみましょう。
Fireの右端の+ボタンを押してAdd Bindingを選択します。プロパティのPathをプルダウンすると実際のデバイス操作の一覧が出てきます。この一覧から探してもいいのですが、ここでListenボタンを押してください。
Listening for Button…という表示に切り替わったら、おもむろにXboxコントローラーのAボタンを押します。すると、Button South [Gamepad]とA [Xbox Controller]という2つの入力の候補が表示されます!
Button Southという概念についてはさきほど説明しました。PS4コントローラー、Xboxコントローラーで十字レイアウトで並んでいるボタンの下側のボタンです。ここでは他のゲームパッドにも対応するためにButton Southを選択するのがよさそうです。
このように、Listen機能を使えば、手元にあるコントローラーのこの操作にアクセスするにはどうすればいいんだっけ、と調べることはもうなくなります。素晴らしい!
バインドを追加したら、プロパティでスキームを設定するのだけ忘れないようにしてください。ここではUse in control schemeのGamepadにチェックを入れます。上のSave Assetボタンを押すと.inputactionsアセットが更新されます。
また、Interactionsでボタンを長押ししたときや複数回押したときにアクションをトリガーするようにしたり、Processorsで数値を加工したりできます。
Add Bindingの代わりにAdd 2D Vector Compositeを使用すると複数の入力を合成して2Dの軸として取得します。MoveアクションにWASDキーがどのようにバインドされているか確認してみてください。
Actionの操作を処理する
さて、こうして定義したActionを実際にアプリケーションで処理するにはどうすればいいのでしょうか。いくつかの方法が提供されています。さきほどGame ObjectにアタッチしたPlayer InputコンポーネントのBehaviorを参照してください。
Send MessageまたはBroadcast Messagesを選択すると、Actionの発生によってこのゲームオブジェクト以下にメッセージを送信します。が、SendMessage系の使用は一般的にバッドプラクティスとされ、パフォーマンスも良くないためここでは説明しません。
BehaviourをInvoke Unity Eventsに変えてみてください。下にEventsというツリーが表示されて、ここからActionの操作で実行するUnityEventを登録できます。下記のようなスクリプトを追加するとイベントを受けられます。
using UnityEngine; using UnityEngine.InputSystem; public class Player : MonoBehaviour { Vector3 move; public void OnMove(InputAction.CallbackContext context) { move = context.ReadValue<Vector2>(); } public void OnFire(InputAction.CallbackContext context) { if (context.phase == InputActionPhase.Performed) { Debug.Log("Fire"); } } void Update() { const float Speed = 1f; transform.Translate(move * Speed * Time.deltaTime); } } |
また、Input ActionsアセットからC#のクラスを生成して、Player Inputコンポーネントを使用せずに状態を直接取得することもできます。さきほど作成したTestInputActionsを選択して、Generate C# Classをチェックし、Applyをクリックしてください。
下記が生成されたクラスを使用してActionにコールバックを登録したり、現在の状態を参照するサンプルです。今度はPlayer Inputコンポーネントは必要なく、このスクリプトのみをアタッチすると動作します。入力処理がC#コードのみで完結し、コードの静的解析が行われて存在しないActionを参照したとき等にエラーが発生するため、最もC#プログラマー好みの方法ではないでしょうか。
using UnityEngine; public class Player : MonoBehaviour { TestInputActions testInputActions; void Awake() { testInputActions = new TestInputActions(); testInputActions.Enable(); testInputActions.Player.Fire.performed += context => Debug.Log("Fire"); } void Update() { const float Speed = 1f; var direction = testInputActions.Player.Move.ReadValue<Vector2>(); transform.Translate(direction * Speed * Time.deltaTime); } } |
Input Debuggerを使う
Input Debuggerウィンドウを使用すると、現在接続されているすべての入力デバイスの状態を調べられます。Windows > Analysis > Input Debuggerメニューで開きます。また、Player InputコンポーネントについているOpen Input Debuggerボタンでも簡単に開けるようになっています。
まずデバイス一覧を表示するウィンドウが開き、デバイス名をダブルクリックするとそのデバイスの状態を表示するウィンドウが開きます。
バーチャルコントローラーを作成する
ここからはスマートフォンへの対応について説明します。
PCではゲームパッドで操作し、iPhone / Androidではディスプレイに表示されたバーチャルスティックとバーチャルボタンで操作するゲームを作りたいという場合があります。
Input Systemには、バーチャルコントローラーの実装を補助するコンポーネントが付属しています。Package ManagerのInput Systemのページで各種サンプルをプロジェクトにインポートできるようになっていますので、ここでOn-Screen ControlsサンプルのImportボタンをクリックしてインポートしてください。
インポートすると、Samples/Input Systemフォルダ以下にOnScreenControlsSampleシーンが追加されます。このサンプルシーンはPlayer Settingsの Player > Other Settings > Active Input Handlingを BothにしないとInvalidOperationExceptionが発生しますので注意してください(EventSystemにアタッチされているStandalone Input Moduleが旧UnityEngine.Inputを使用しているためです)。
エディタでシーンを再生すると、マウスでバーチャルコントローラーを操作できます。鍵となるのはCanvasのLeft StickとRight StickにアタッチされているOn-Screen Stickコンポーネントと、各ボタンにアタッチされているOn-Screen Buttonコンポーネントです。
さきほど紹介したInput DebuggerをWindow > Analysis > Input Debuggerで開いてみてください。DevicesのGamepadをダブルクリックで開き、GameウィンドウのLeft StickやRight Stickをドラッグで操作するとGamepadのスティックの状態が変化し、イベントが送られてくるのが見えるはずです。
以上から分かる通り、Input Systemを使用して通常通りゲームパッドからの入力を受けつけるようにゲームを作っていれば、そのままの形でバーチャルコントローラーに追加対応できるようになっています。
タッチ入力を取得する
iPhone / Androidのタッチ操作を取得するAPIとして、低レベルAPIのTouchscreenと高レベルAPIのEnhancedTouchがあります。旧UnityEngine.Inputと同じようにタッチ操作をトラッキングするにはEnhancedTouchを使用します。
サンプルスクリプトです。EnhancedTouchSupport.Enableで有効にする必要があります。
using UnityEngine; using UnityEngine.InputSystem.EnhancedTouch; using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch; public class EnhancedTouchExample : MonoBehaviour { void OnEnable() { EnhancedTouchSupport.Enable(); } void OnDisable() { EnhancedTouchSupport.Disable(); } void OnGUI() { var text = ""; foreach (var touch in Touch.activeTouches) { text += $"Touch: Id {touch.touchId} Position {touch.screenPosition} Phase {touch.phase}\n"; } GUILayout.Label(text); } } |
Touchクラスを使用する際に、旧来のUnityEngine.TouchとUnityEngine.InputSystem.EnhancedTouch.Touchのどちらを参照するかが曖昧なため、usingステートメントで後者を明示的に指定していることに注意してください(Gyroscopeクラスの使用でも同様の問題が発生します)。
新しいInput Systemは現時点では基本的なタッチ入力しかサポートしていませんが、将来的にはジェスチャー入力等より高度な表現にも対応する予定とのことです。
加速度センサー等の入力を取得する
加速度センサー等の入力は下記の要領で取得できます。最初にInputSystem.EnableDeviceを呼んでデバイスを有効化する必要があります。
using UnityEngine; using UnityEngine.InputSystem; public class AccelerometerExample : MonoBehaviour { void OnEnable() { InputSystem.EnableDevice(Accelerometer.current); } void OnDisable() { InputSystem.DisableDevice(Accelerometer.current); } void OnGUI() { var text = $"Accelerometer: {Accelerometer.current.acceleration.ReadValue()}"; GUILayout.Label(text); } } |
ローカルマルチプレイ機能について
Input Systemにはローカルマルチプレイゲームの開発を補助する仕組みが備わっています。ローカルマルチプレイゲームというのは、ネットワークを使用せず、1台のPCやゲーム機に複数(ときには多数)のコントローラーを接続して多人数で遊ぶタイプのゲームのことです。
Package ManagerでInput Systemパッケージのページを開き、Simple Multiplayerサンプルをインポートして、SimpleMultiplayerPlayerシーンを開いてみてください。
鍵となるのはPlayerManagerオブジェクトにアタッチされているPlayer Input Managerコンポーネントです。Join Behaviorプロパティにより、キーボードやゲームパッドのボタンが押されるとPlayer Prefabで設定したプレハブを生成してジョインするようになっています。ゲームの実行中に新しいゲームパッドを挿してジョインすることも可能です。
なお、このSimple Multiplayerサンプルにはプレイヤーの移動等は実装されておらず、複数のコントローラーからジョインできることを示す最小限のデモですので注意が必要です。プレイヤーの移動等を含めたサンプルとしてはGitHubでWarriorsプロジェクトが公開されています。
さらに、Player Input Managerコンポーネントにはプレイヤーごとに画面を分割して表示する機能があります。コンポーネントのEnable Split-Screenフラグ、およびPlayer InputコンポーネントのCameraにより新しいプレイヤーがジョインするたびに画面が分割されていきます(これがPlayer InputコンポーネントにCameraプロパティがある理由です!)。
この画面分割の機能は、入力システムの責務から逸脱していて少々やりすぎのようにも思えますが、例えばゲームジャムでローカルマルチプレイゲームを作りたいといったシチュエーションで活躍するのではないでしょうか。
なお、Player InputのCameraについて、現時点ではCinemachineのカメラは使用できないため注意してください。また、Xboxコントローラー等XInput方式のゲームパッドはXInput自体の制限により4個までしか認識しません。HIDジョイスティックは何個でも接続可能です。
まとめ
以上、Unityの新しいInput Systemの概要と、特に注目すべき機能について紹介しました。まだ発展途上の部分もありますが、非常に高機能なシステムで、最近の標準的なゲームパッドにデフォルトで対応していることや、バーチャルコントローラーを簡単に実装できることだけでも使う価値が感じられると思います。今後のUnityの標準システムとなる予定ですので、機会があればぜひ導入してみてはいかがでしょう。
よくある疑問点
Q:ゲームパッドを振動させるには?
GamepadはIDualMotorRumbleインターフェースを実装していて、SetMotorSpeedsメソッドで振動させられます。
Q:DOTSはサポートされていますか?
version 1.0.2時点ではサポートされていません。サポート予定ですが、2021年5月現在は明確なスケジュールはないようです。
Q:旧Input.GetAxisのように慣性のついた入力を取る方法は?
version 1.0.2時点では存在しません。
Q:XRデバイスのサポートは?
現在、Unityの新しい標準XRシステムであるXR Interaction Toolkitが開発されており、新しいInput Systemが使用されています。導入手順が若干複雑なため、動作するサンプルプロジェクトとしてXR Interaction Toolkit Exampleを開いてみるのがおすすめです。
なお、現時点ではOculus QuestのハンドトラッキングやSteam VRのトラッカーの判別等に対応していません。ヘッドセット固有の機能を使用したい場合は、Oculus IntegrationやSteamVR Plugin等、各プラットフォームのSDKを使用したほうがいいでしょう。
Q:Button North等のボタンの実際の名前を取得するには?
Package ManagerでInput SystemパッケージからIn-Game Hintsサンプルをインストールしてみてください。移動して箱を持ったり、投げたりその場に落としたりできるサンプルです。使用しているコントローラーによってヘルプメッセージのボタンの表示名が変わることを確認してください。InGameHintsExample.csでGetBindingDisplayStringメソッドを使用して、アクションの現在のコントロールスキームの表示名を取得しています。
Q:UGUIの入力に新Input Systemを使用するには?
UGUIのEventSystemオブジェクトを開くと、Standalone Input Moduleコンポーネントについて「Replace with InputSystemUIInputModule」というボタンが出てきますので、クリックしてコンポーネントを入れ替えてください。各種操作がDefaultInputActionsに割り当てられます。
Q:入力デバイスの種類を制限するには?
Project Settings > Input System Packageを開いてCreate settings assetボタンを押して設定ファイルを作成してください。下のSupported Devicesのところにデバイスを追加すると入力デバイスを制限できます。空だとすべてのデバイスからの入力を取得します。
筆者紹介:こりん(古林 克臣)
株式会社エクシヴィ シニアソフトウェアエンジニア。2013年にOculus Rift DK1を入手し、以降「フレームシンセシス」という活動名でVRコンテンツの自主制作やイベント出展をしていたらいつのまにか本業に。4年間の施設・イベント向け商業VRコンテンツの制作を経て、2019年12月より株式会社エクシヴィにてOculus Quest版「AniCast Maker」の開発に参加。著書に「VRコンテンツ開発ガイド2017」(共著)。XR Kaigi 2020にて登壇。
・Twitter:https://twitter.com/korinVR