見出し画像

[Unity]InputSystemがちゃんと動く忘備録

ゲーム開発も大詰めになって、今までタッチパネル用インターフェースだったのをゲームパッドに対応するべく、InputSystemを実装しましたが、
ネットで見つけた記事を参考にしても動かない!?で苦しんだ末、やっと動いたのでメモを記します。

PlayerInput?インターフェース??何したらいいの?

InputSystemの最大のつまずきポイントは、導入に複数のルートがあり混乱するという点でした。
パッケージマネージャーからInputSystemをインポートしてUnityを再起動、
まではまあ共通として、そこからの手順が…

・InputManagerと共存する
・設定ファイルを作成する
・コーディングして直接動かす
・InputActionを作る
・InputActionの取得スクリプトを書く
・EventSystemをシーンに入れる
・PlayerInputをアタッチする
・インターフェースを作る


などなど…InputManagerと比べてとてもたくさんあってうんざりしました…

…が、これらは実は、
状況によって取捨選択するものであり、すべてやらないとInputSystemは使えないわけではない
のでした。

そんなこと知識0から初めて、ネットで適当に見つけた記事を読みながら導入する立場では知り得ない話であり、
「あれ動かないぞ、なにか足りないのかな?別の記事見たら、知らん情報が出てきた?!これならどうだ!」
で、ものすごく遠回りして全部のせしてあがいた末に動かなくて、心を折られます。

それぞれの用途使い所を列挙すると、

・InputManagerと共存する…必須ではないけど、タッチパネルやマウス操作も使う場合で、単純な操作だけならInputManagerのほうが楽です。InputSystemのタッチ操作は学習曲線が未知数だし動く保証はないし。

・設定ファイルを作成する…多分絶対必須です。

・コーディングして直接動かす…InputActionなしでやるとかメリットが思いつかないです。無視していいはず

・InputActionを作る…必須。使わないメリットが思いつかないです

・InputAction取得スクリプトを書く…必須ですが、InputSystemはアクションを取得するためのコードがいくつもあり、これも混乱のタネです。

・EventSystemをシーンに入れる…タッチパネルを使うのなら必須です。使わないなら多分いりません。

・PlayerInputをアタッチする…これをアタッチしたオブジェクトの全コンポーネントがInputActionの恩恵を受けられるようなので必須です。Invoke Unityイベントで、手軽に操作できます。またInputActionの新規作成がちょっと楽になります

・インターフェースをコーディングする…Actionを自作する=Actionのプリセットに無い特殊なデバイスや操作を使うなら必要なのかも。普通のゲームパッド操作の実現が目的ならおそらく不要です。

普通のキーボード+マウス/ゲームパッドの動作がほしいだけなら、インポート~設定ファイルまでのセットアップと、InputAction作成と、PlayerInputアタッチと、取得スクリプト作成、だけでいいです。タッチ操作するならInputManagerとEventSystemを追加です。

InputActionにゲームパッドを追加したのに効かない

InputActionの編集画面のBindingに、GamepadがすでにセットされてるところにXBOXコントローラーやPSコンを追加したら、競合して効かなくなるみたいでした。Gamepadだけで箱コンは問題なく動きました。これがスマホでも動くのか、どんなゲームパッドでも行けるのかはわかりません。Supported Deviceの設定と関係があるかもしれません。
また、PlayerInputからInputActionを新規作成すると、1つのボタンにFire1,Fire1Hold、Fier1Releaseと3つもアクションがあるのですが、名前だけで中身はなく、自分でBindingとInteractionを書かないと機能しません。
それぞれ押した瞬間、長押し、長押し解除と分けて設定できるのでしょうが、適切な書式がわからんうちに下手にFire1Hold、Fier1Releaseの中身を書いたら競合しかねません。

動くけど動かない

プロジェクト設定からSupported Deviceにタッチパネルやゲームパッドを登録しないと、まともに動きません。
言い換えると、
未登録でも一部のボタンは中途半端に動くため、これに気づくのが遅れました。何週間も。
参考記事

ただ、これを設定すると、「デバイスが見つかりません」的なエラーが出ることがあるようです。


InputActionをセーブしたらいっぱいエラーが出た

InputActionをエディタで編集してセーブすると、InputActionのc#コードが書き出されます。

中身はこんなん

これが、なぜか書き出し設定がおかしくなって別の場所に同じコードが書き出されてスクリプト重複エラーが出ました。余計なことしなければ多分起こらないトラブルだと思いますが。

これで動いた

InputActionの取得コードは、なんでかいくつもあります。高次元な操作を実現するなら違いが出るのでしょうが、普通のジョイパッド操作する分にはわからない話で。
こちらの記事の方法が、一番わかりやすく、確実に動きました。

正確なコードは記事を見てもらうとして、
using UnityEngine.InputSystem;

InputAction _moveAction, _lookAction, _fireAction;

var pInput = GetComponent<PlayerInput>();
var actionMap = pInput.currentActionMap;
_moveAction = actionMap["Move"];
とやると、
Vector2 move = _moveAction.ReadValue<Vector2>();
で入力から値が取れます。

ボタン+LRトリガーの場合は
bool fire = _fireAction.triggered;
で押した瞬間だけ取得できます。
float fire=_fireAction.ReadValue<float>();
とやれば、ボタンから0-1の値が毎フレーム取得できます。LRトリガーの場合は小数の値がとれます。
どちらも同時に取得できます。

このコードだと長押しやダブルタップとかは未対応みたいで、
長押し判定を自分で書く必要がありますが、Supported Deviceに未記入でも割とまともに動いたので、信頼性が高いと思います。
ゼスチャーとかコマンド技とか複雑繊細な操作を必要とするならこれで足りるかわかりませんが、普通のゲームパッドの操作がほしいだけならこれでいけました。

どう使うといいのか

InputSystem・InputActionの設定と導入を完璧にできたとして、シーンのオブジェクトを動かすには、そのオブジェクトにアタッチされてるコンポーネントがInputSystemに対応してて、入力を検知するコードを書く必要があります。
PlayerInputをオブジェクトにアタッチすれば、sendmessageモードで特定の名前のメソッドを作動させられ(どんな名前かは、モード選択UIの下にすげえちっさい字で書いてあります)、Invoke Unityイベントモードで外部から任意のオブジェクトをノンコードで操作できます。InputManagerではできないことですね。

(追記:タッチ操作の場合は、バーチャルパッドのUIとOnScreenコンポーネントなどと同じ場所にPlayerInputをアタッチすると、タッチ操作を受信することができます。)
でも、ちゃんとしたゲームを作るには役不足です。
キャラクターを手軽に快適に動かせたとしてその次、メニューを開いた時だけ動けなくするとかで困ることになります。乗り物に乗ったりメニューを開いたら専用のキーアサインに切り替えたいし、ゲーム内のオプションから制限付きのキーアサインもできるようにしたくなるでしょう。
(結局、デバイスごとにUIを作り切り替える処理は作らないといけない)

コーディングしたくないあまり違法建築めいて複雑にイベントを組み合わせたり、ジャンプとか攻撃とかの各コンポーネントごとに独立して入力判定したり、かっこつけてインターフェースで疎結合とかやると、デバッグで仇になります。
特に、複数箇所で入力判定はマジで極力避けるべきです。

すべての入力と数値取得を一箇所でまとめて管理するコンポーネントを作り、それをゲームマネージャーとかに登録、
操作対象の各コンポーネントはゲームマネージャー経由でアナログスティックからvector2を、ボタンからbool・floatを取得して、
ついでにメニュー開いてるときなど状況次第で取得を禁止したりキーアサインを切り替えたりもまかせて…って作れば、思い通りに作れるし管理しやすいのではと思います。
私のいうゲームマネージャーの仕様については、こちらの記事の下の方で語ってます。

で、ジョイパッドUIメニューを作るにはどうしたら

できればタッチパッド用のUIをあまり改造せず流用したい…!
でもゲームパッドで移動するUIメニューの作り方、情報少ない…!
やっと見つけた記事には、EventSystemとUIボタンのNavigationを使う方法がありました。
参考記事


EventSystemには最初に選択状態になるボタンオブジェクトを設定できる項目があります。

これを設定すると、あとは近接したUI ボタンを十字キーでたどれる挙動ができます。
カスタムする場合は、UIボタンやタブやスライダーのナビゲーションを「Explicit」にすると、ボタンの接続先を指定できます。

可視化のボタンで、UIのつながりが表示されます。

が、EventSystemの選択オブジェクトはメニューが非アクティブだと効かないぽいので、現実にはあまり恩恵は…
かわりに、メニュータブを開いた時のイベントでUIボタン.select()を実行させると、そのUIが初期選択状態になります。ただ、けっこうな確率で表示状態が未選択のままなことがあります。(原因は謎)

…思ったより手軽にUIが作れるなあと喜んだのも束の間、
LBRBなどでタブきりかえたい、実行時に自動生成されるボタン、画面に収まりきらない量のボタン、などで頭を抱えることになります。

UIの移動は、十字キー移動以外は対応してません。
LBRBの場合は、LRボタンを押すとカレントタブが変わるようなコードを、普通に作ることになります。タブはToggleGroupにしとけば.isOnで切り替え実行させられます。これに関してはジョイパッド+タッチ操作+キーボード操作がまあまあ簡単にできるので問題ないですが(いや、ほんとは細かい問題山積みですが)、
あとの2つは超クソ大変です。
selected()をノンコードで指定できない、自動生成されたボタンのNavigationは意図しないUIにリンクされて制御不能すぎるから接続するコードを書かないといけない、タッチ操作なら必要なかったUIのスクロールも自力で実装しないといけない、そしてスクロールは意味不明かつ複雑な仕様で使える情報がマジで出てこない、など、地獄です。
私はスクロールで心折れました。
UnityのGUIはジョイパッドに対して殺意がある。

UIの作り方については、あまりInputsystem関係ない話になると悟ったので、解説はこの辺で終わりにします。

この記事が気に入ったらサポートをしてみませんか?