【Unity】新しいInput Systemの使い方
あけましておめでとうございます。 今年もよろしくおねがいします。
さて、今回は新しいInput SystemがPackage Managerで使用できるようになったので、実際に使い方を確認してみました。
なおPreview版なので、動作が変化するかもしれません(本記事は0.1.2のバージョンで作成されています)
- 更新記事
- 新しいInput Systemの導入
- 新しいInput Systemの利用(InputControl)
- 新しいInput Systemの利用(InputAction)
- 新しいInput Systemの利用(InputActions)
- 感想
- 関連
更新記事
新しいInput Systemの導入
まずは新しいInput Systemを導入してみます。新しいInput Systemは古いInput Systemとの共存も可能ですが、今回は共存しない方向でいきます。
必要な項目のリストは以下の通り
Script Runtime Version
を.NET 4.x
に変更Active Input Handring
をInput System (preview)
に変更Package Manager
でInput System
を導入
まずScript Runtime Version
とActive Input Handring
を.NET 4.x
とInput System (Preview)
に変更します。この2つの作業はそれぞれエディターの再起動を要求します。
あとはPackage Manager
でInput System
をインポートします。もし一覧にない場合はPacakgeManager内のAdvanceボタン
からShow Preview Package
を有効にします。
これで準備完了、早速試してみます。
新しいInput Systemの利用(InputControl)
新しいInput Systemを使ってみます。
最初は比較的以前のInput Manager
と似たようなアプローチでInputControl
ベースの機能です。下のコードはマウスの位置とスペースキーが押されているかどうかをログに表示します。
今回紹介するのは基本的なフレーム毎に情報を取得するスタンスで、動作はフレームレートに依存します。
Mouse.current
で現在のマウスを取得、position.ReadValue()
で現在のマウスの座標しています。またKeyboard.current
でキーボードを取得し、spaceKey.isPressed
で押されているかどうかを取得します。isPressed
の他にもwasPressedThisFrame
(今のフレームで押したか)やwasReleasedThisFrame
(今のフレームで離したか)といった情報も取得が可能です。
この例ではマウスとキーボードですが、同様の手順でJoystick
、Accelerometer
、Gamepad
、Gyroscope
、Joystick
、Pen
、Touchscreen
等にアクセスするためのAPIが用意されています。
逆をいえば、GamePadとKeyBoardの両方で入力を受け付ける場合には両方から情報を取得する的な工夫が必要そうです。
また入力を取得するデバイスはProjectSettings > Input(New)
のSupported Devices
から指定出来るみたいです。指定がなければ取れるモノは全て取得します。
ただし、自分がこれを変更するとエラーになるので、バグっている、もしくは何か条件がありそうです。
新しいInput Systemの利用(InputAction)
次に新しいInput SystemのInputAction
ベースの入力取得についてです。こちらのアプローチは今までのInput Manager
ベースと異なり、イベントで動作します。これは入力を行う度にイベントが呼ばれるというスタンスで、事前にデリゲートに処理を登録しておく必要があります。
この処理で面白いのは1フレームに複数回の入力を受け取れるという点です。フレームに依存せず入力を受け取れる為、究極的に言えば処理落ちした場合でも入力がスキップしないという期待が持てます。
例えば下のGifアニメでは、低フレームレートの環境でマウスの位置を、Input Manager(現行)
とInput System
の両方で取得して線を引いています。Input Manager
はフレーム開始時のマウスのみを取得しているために非常に単純な図形しか描けないのに対して、Input System
はフレームの中間の入力も取得出来るのでマウスの動かした通りの線が引けています。
なお、非同期で入力を取得出来るかは、現状プラットフォームに依存します。これは未完成なのかもしれませんし、デバイス的な成約かもしれません。また、フレームに依存せず非同期にコールバックを受けている用に見えますが、実際にはバッファに入力を溜めておき、フレームの頭に一気に入力を処理するという流れみたいです(フレーム間の処理を利用するならば、バッファの中の時間を元に動作を再現するような実装が必要)
実際にInput Systemを使ってみます。
まずはInputAction
フィールドを用意し、inputAction.performed
に入力を受けた時の動作を記述します。下の例の場合、mouseInput
(マウスを動かした時に動作する想定)にはマウスの座標を、fireInput
(ボタンを押す想定)には押したら反応するように設定します。
なお入力は定義しただけでは動作しないので、inputAction.Enable()
で有効化します。
次にエディター側の操作で、ユーザーの操作と入力を関連付けます。これはInspector側で行います。+
でInputAction
とバインドする操作を追加し、Binding
で登録する項目を追加、ダブルクリックでメニューを開き、実際にバインドする入力を選択します。
もしGetInputAxisのように'Aキー'と'Dキー'の2つをバインドしたい等の場合は、Binding
ではなくCompsoite>Axis
等を選択します。
これで実行すればマウスの位置とクリックの情報をログに出してくれますが、このままだとMouse DownとMouse Upの両方のイベントを受け取る事になります。なので、InputAction
のInteractions
にPress
を設定し、押した時にしか(もしくは離した時にしか)イベントを呼ばれないようにして、この問題を回避します。
新しいInput Systemの利用(InputActions)
最後にInputAction
を複数まとめたinputActions
を作り、多くのコントローラーで使い回す事をしてみます。
Input Action
には複数の入力やバインド情報がまとめられる他、入力時の動作実装を楽にするコードを生成する機能があります。
実際の手順を見てみます。今回もマウスの位置とクリックの入力を取得するコードを生成します。
Assets > Create > Input Actions
でInput Actionアセットを作成します。名前はMyInputSample
とします。MyInputSample
をダブルクリックで開いて、Action Maps
の+
をクリック、Player
を作成します。Actions
の+
をクリック、バインドする入力を登録していきます。Save Asset
ボタンを押して、バインド画面を閉じます。-
MyInputSample
を選択後にGenerate C# Class
にチェックを入れ、Generate Interface
をクリックします。 Apply
ボタンを押します。
これでApplyボタンを押した後にInputActionsと同じ名前のコード
が自動的に生成されます。
次はC#側の実装を行います。
まずMyInputSample
…先程生成されたクラスをフィールドに登録します。またMyInputSample
も有効にしないと動作しないので、input.Enable()
とinput.Disable()
で有効・無効を切り替えておきます。
動作の登録はインターフェースを使います。Action Map
を作ると自動的にI
+ActionMap名
+Actions
なるインターフェースが作られるので、継承します。今回の場合はSample
なのでISampleActions
が自動生成されます。後はメソッドの中身を埋めてinput.Sample.SetCallbacks(this);
で自身(の持つコールバック)を登録します。
最後にMyInputSample input
にMyInputSample(アセット)
を登録してやれば準備完了です。マウスを動かすとログが表示されます。
Control Schemaの追加
プラットフォーム毎に違う入力がある場合は、Control Schema
で作れそうです。例えば上はキーボードとマウスの動作ですが、ゲームパッドの動作を対応させようと思います。
No Control Schema
を選択し、Add Control Schema
を選択します。+
ボタンを押し、GamePad
を選択Add
を押す
これでゲームパッドを利用中の場合のキー操作が作れました。
この調子でVR機器やタッチパネル等、複数の端末に対応させるのが良さそうです。
動的なキーバインドの変更
アクションマップはテキストで制御されているので、差し替える事が出来ます。
例えば上でPC Schemaを追加した状態でFire
の動作をマウスクリックからSpaceキーに変更する場合は、下のようなコードを記述します。
input.Sample.Fire.ApplyBindingOverride("<Keyboard>/space", input.PCScheme.bindingGroup);
この<Keyboard>/space
の部分を確認する一番手っ取り早い方法は...
ボタンを押すことですが、WIndow > Input Debuger
のレイアウトから確認するのも良さそうです。コチラの場合は、実際にボタンを押せるのかといったレベルで確認出来ます。
毎フレーム入力の取得したい場合
InputActionは「操作が変化した瞬間」のみイベントを呼び出します。つまり押しっぱなしで移動するようなケースの場合にうまく動作しません。なので、トップのGifアニメのようなキャラクターを動かすような、毎フレーム入力を取得するようなアプローチに使用すると面倒な所があります。
その対策を色々と確認していますが、現状一番手っ取り早いのは下のようなアプローチです。
bool push; public void OnFire(InputAction.CallbackContext context) { push = context.ReadValue<bool>(); }
もしくは下のようなコードでも取得出来ました。下の例ではUpdate
のタイミングでマウスの位置を取得し続けます。
void Update() { var control = input.Sample.MousePosiiton.lastTriggerControl as Vector2Control; if (control != null) { Debug.Log(control.ReadValue()); } }
controls
でも入力は取得できますが、全てのスキーマの入力を取得してしまったのでlastTriggerControl
の方が楽かなという印象です。
感想
概ね問題なく使えそうです。現状まだ完全にプレビューで未完成の部分がかなり多いですが、そのうち完成するんじゃないかなと。
今回は上辺だけ書きましたが、もう少しバージョンが進んだらもう少し込み入った部分も書いていきたい所です(バッファに入力を流し込んでる所とかの話)
関連
使い方の動画です
アップデート最新情報系
Input System Update - Unity Forum