あけましておめでとうございます。 今年もよろしくおねがいします。

さて、今回は新しい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