nearlanのブログ

ただのメモ ちゃんとした記事は別で

InputSystemを使えるようにするメモ

とある移動用スクリプトを試そうと思ってけどそれ以前の問題に突き当たってしまったので

 

Unity自体をしばらく使ってなかったので、その間にそれまでのInputManagerがレガシー化して新しいInputSystemが登場。それをPackage Managerからインポートして使うわけだけど、古いプロジェクトだとどうもそれがスクリプトに上手く反映されず(UnityEngine.InputSystemが認識されない)、結局最新のLTSで新しいプロジェクトを作り直して、まずはInputSystemの基本仕様を確認することに。

 

自分でも何を覚えていて何を知らないのかを忘れてるので、整理されてない散らかった文になる可能性大

 

大体以下の動画のトレース(~13分くらいまでがこの記事の内容)

www.youtube.com

 

 

Input Actionsを作る

まずAssestの中で右クリックして「Input Actions」を作る。この時点で既に旧式のInputとやり方が違う。知らなかったら気づかなそう

この中で入力に関連する変数とかを個別に設定できるので、旧式より取り回しが良くなる、というのはわかる

 

Actions Properties

ActionMapsで新規のActionMapを作って「Player」とする。Actionsは「Jump」とし、ジャンプ用の設定を作る。

Acition Typeの「Button」はもちろんボタン、「Value」はジョイスティックなどの入力され続ける値、「Pass Through」はValueの特殊な使い方らしいがとりあえず置いておく。今回はジャンプなのでButtonを使う

Interactionsではホールドやタップといったボタンの押され方の設定ができる。今回はそのままにしておく

Binding Properties

JumpのプルダウンにBinding、キーバインドの設定がある。Binding->Pathのプルダウンに設定するパッドなりマウスなりのボタンを選べる。テキストで直接指定もできるらしい

「Listen」を押すことで、その直後に押したキーやボタンが認識されて自動的に絞り込まれる。たぶんこれが楽。ここではSpaceキーを選択

 

これでごく基本的なInputの設定が出来たので、「Save Asset」で保存する。左上のタブに*があったらまだ保存されてないので注意。その横に「Auto-Save」のチェックボックスもある

 

Sceneのセットアップ

Inputを試すためのテストSceneを用意する。デフォルトのPlaneとSphereを追加、特に弄らない

Player Inputの設定

SphereにAdd Componentから「Player Input」を追加。ここのActionsに先ほど作ったPlayerInputActionsを適用する。ActionMapは1個しか作ってないのでDefault Mapはそのまま。UI Input ModuleやCameraは今は使わない

Behaviorは主にスクリプトでのアクションの取得方法を決める。一般的には「Invoke Unity Event」が使われるらしい

Eventsにはデフォルトでは3種類のEventが表示される。「Device Lost Event」は例えばパッドが外れた時、「Device Regained Event」はそのパッドが再接続された時、「Controls Changed Event」はパッドからキーボードなどに切り替わった時をそれぞれ表す。

先ほど作った「Player」のイベントも作られているので、そこの+ボタンで設定を行う(まだやらない。ここに適用する簡単なスクリプトを用意する

簡単なスクリプトのテスト

C#スクリプト「testingInputSystem」を作り、Sphereに追加する。内容はこれだけ↓

public class testingInputSystem : MonoBehaviour
{
    public void Jump() {
        Debug.Log("Jump!");
    }
}

SphereのPlayer Input->Events->Playerの+ボタンを押して、下の項目にはSphere自身を、右の項目にtestingInputSystem->Jumpを設定する。この時、Playerではなく他のDevice Lost Eventなどに間違って設定しないよう注意(1敗)

これでSceneを再生してスペースキーを押すとConsoleのログにJump!と出るはず

ログだけではアレなので実際にジャンプさせるコードも追加↓ Rigidbodyが無かったら付けておくのを忘れずに

public class testinginputsystem : MonoBehaviour
{
    private Rigidbody sphereRigidbody;

    private void Awake() {
        sphereRigidbody = GetComponent<Rigidbody>();
    }

    public void Jump() {
        Debug.Log("Jump!");
        sphereRigidbody.AddForce(Vector3.up * 5f, ForceMode.Impulse);
    }
}

ボタンを押したときだけジャンプする

なおこの時ログを見ると、ジャンプを1回するたびに3回表示されてるのが分かる。これは「最初に押された時」「現在押されている時」「離された時」の3回の判定が行われているから。

このことを実際に確認するためにJump関数の引数にCallbackContextを追加する。(使うためにInputSystemの宣言が必要)

using UnityEngine;
using UnityEngine.InputSystem;

public class testinginputsystem : MonoBehaviour
{
    private Rigidbody sphereRigidbody;

    private void Awake() {
        sphereRigidbody = GetComponent<Rigidbody>();
    }

    public void Jump(InputAction.CallbackContext context) {
        Debug.Log("Jump!" + context.phase);
        sphereRigidbody.AddForce(Vector3.up * 5f, ForceMode.Impulse);
    }
}

これでSceneを再生してまたspaceでジャンプすると、ログに"Jump!Started"(押した), "Jump!Performed"(押している), "Jump!Canceled"(離された)と判定の順番に表示されるのが分かる。

これを一般的な実装のように、ボタンを押したときだけジャンプの処理が行われるように変更するため、Jumpにif文を追加する

public void Jump(InputAction.CallbackContext context) {
        if (context.performed) {
        Debug.Log("Jump!" + context.phase);
        sphereRigidbody.AddForce(Vector3.up * 5f, ForceMode.Impulse);
        }
    }

これでもう一度Sceneで確認すると、判定が1回分だけになり、その分ジャンプする高さも下がる

C# Event

同じことをC#イベントを使って実装する場合を考える(実装するところまではやらない)。Player Input->Behaviorを「Invoke C# Event」に切替。コンポーネントからイベントを設定していたのを、代わりにスクリプト内で行う

public class testinginputsystem : MonoBehaviour
{
    private Rigidbody sphereRigidbody;
    private PlayerInput playerInput;

    private void Awake() {
        sphereRigidbody = GetComponent<Rigidbody>();
        playerInput = GetComponent<PlayerInput>();

        playerInput.onActionTriggered += PlayerInput_onActionTriggered;
    }

    private void PlayerInput_onActionTriggered(InputAction.CallbackContext context)
    {
        Debug.Log(context);
    }

    public void Jump(InputAction.CallbackContext context) {
        if (context.performed ) {
        Debug.Log("Jump!" + context.phase);
        sphereRigidbody.AddForce(Vector3.up * 5f, ForceMode.Impulse);
        }
    }
}

変更点はPlayerInputクラスを使った処理の追加。詳しい仕様については後でリファレンスを見るけど、1つにまとまったInputActionのイベントを格納できるので、この関数の返り値をそのまま代入する書き方でいいらしい。

Scene再生で確認すると、Spaceキーを押したときの情報が↓のようにログに表示されているのがわかる。

action=Player/Jump[/Keyboard/space] phase=Started time=8.15317470000082 control=Key:/Keyboard/space value=1 interaction= }

 action=Player/Jump[/Keyboard/space] phase=Performed time=8.15317470000082 control=Key:/Keyboard/space value=1 interaction= }

action=Player/Jump[/Keyboard/space] phase=Canceled time=8.28674000000319 control=Key:/Keyboard/space value= interaction= }

Invoke Unity Eventと同じことをする場合、この情報をもとにスクリプトからphaseを識別して、それに応じた処理を書くことになるらしい。動画では、今回のような目的ではInvoke Unity Eventを使うのが適切だと結論付けられてる。(Behaviorを元に戻す)

 

とりあえずここでいったん区切り