【Unity】Input SystemのProcessorで入力値を加工する

こじゃらこじゃら

スティック入力を反転させたり、感度を調整する方法を知りたいの。

このはこのは

Input SystemのProcessorを使えば実現できるわ!

Input SystemProcessor機能の使い方についての解説記事です。

Processorを使うと、次のように入力値に対する加工が可能になります。

Processorで出来ること
  • 値を反転する
  • 値に対してスケールを掛ける(感度を調整する)
  • デッドゾーンを設ける
  • 自作の加工処理を施す

参考:Processors | Input System | 1.3.0

ProcessorはInput Actionの機能の一つです。そのため、Processorを使うにはInput Actionを使用している必要があります。

本記事では、Input SystemのProcessorを用いて入力値を加工する方法について解説します。

動作環境
  • Unity 2021.2.18f1
  • Input System 1.3.0

スポンサーリンク

前提条件

UnityプロジェクトにInput Systemパッケージがインストールされ、Input Actionの設定が済んでいるものとします。

Input Systemパッケージのインストール方法が分からない方は、以下記事を参考に準備を進めてください。

また、Input Actionの設定方法が分からない方は、以下記事をご覧ください。

Processorの仕組み

Input SystemのProcessorは、入力値を加工するための機能です。

参考:Processors | Input System | 1.3.0

Processorには、予め次のような種類が用意されています。

Processorの種類一覧
  • Clamp
  • Invert
  • Normalize
  • Normalize Vector 2
  • Normalize Vector 3
  • Scale
  • Scale Vector 2
  • Scale Vector 3
  • Axis Deadzone
  • Stick Deadzone

これらのProcessorでは物足りない場合、Processorを自作することも可能です。

例えば、ScaleというProcessorは、入力値に係数を掛ける加工を行います。

Scale Vector 2の例

Processorが受け取れる入力値の型は決まっています。

入力値に対して適用するProcessorは、入力値の型(Control Type)Processorが要求する型(Operand Type)一致している必要があります。

入力を渡せる場合・渡せない場合

例えば、2軸入力を処理するProcessorであるScale Vector 2は、Operand TypeがVector2型であるため、ボタンのような1軸入力(Control Typeがfloat)を処理することが出来ません。

また、Processorは一つのActionまたはBindingに対して複数適用することが可能です。

複数適用した場合、Processorの登録順に処理が実行され、バケツリレー方式で順番に値が渡されていきます。

Processorの設定手順

予め作成したInput Actionアセットファイルダブルクリックし、ウィンドウを開きます。そして、Processorを適用したいActionまたはBindingクリックで選択しておきます。

すると、右側に表示されるAction Properties下にProcessorsの設定項目が表示されます。

Processorの追加は、Processors右の+アイコンから行います。一覧には、Control Typeに適合したProcessorのみが表示されます。

例では、スティック入力を反転するためのInvert Vector 2のProcessorを追加することとします。

追加すると、設定項目が現れますので、必要な設定を行います。設定内容は後述します。

Save Assetボタンをクリックすると、Input Actionアセット内に設定内容が保存されます。

Processorの一覧

Input Systemには値を反転させたり、スケールを掛けたりといったProcessorが予め用意されています。一通り紹介していきます。

各Processorが実際に処理可能な入力値の型とパラメータは、以下公式リファレンスから確認できます。

参考:Processors | Input System | 1.3.0

Clamp

値を指定された範囲に丸めるProcessorの一種です。

値を下限(Min)から上限(Max)の間に丸めます。

Invert

入力値の符号を反転するProcessorです。float型のほか、Vector2Vector3に対応したものも存在します。

float版には設定項目がなく、Processorを追加するだけで反転処理が適用されます。

Vector2とVector3については、ベクトルの各要素に対して反転の有無を指定できます。

Normalize

入力値を指定された範囲に正規化するProcessorです。

入力値がfloatVector2Vector3の三種類のバリエーションが存在しますが、float型だけ動作が特殊な点に注意する必要があります。

floatの場合

下限(Min)を0、上限(Max)を1として、その間で値を正規化します。

Zeroはニュートラルの値を示すもので、ZeroがMin以下の場合は0~1の範囲で正規化、ZeroがMinより大きい場合は-1~1の範囲で正規化されます。

例えば、Minが0、Maxが5、Zeroが0、入力値が1の場合、出力値は0.2になります。
Minが-10、Maxが10、Zeroが0、入力値が-5の場合、出力値は-0.5となります。

したがって、Processorの内部の計算は次のような実装になります。

Normalize Processorの内部計算

パーセンテージ = (入力値 – Min) / (Max – Min)とすると、

  • Min Zeroの場合
    • 出力値 = 2 × パーセンテージ – 1
  • それ以外の場合
    • 出力値 = パーセンテージ

Vector2、Vector3の場合

float版とは違い、入力値のベクトルを長さ1に正規化する処理を行います。内部的には、Vector2.normalizedVector3.normalizedの値をリターンしているだけです。

設定するパラメータは特にありません。

ベクトル正規化の内部処理について詳しく知りたい方は、以下記事をご覧ください。

Scale

入力値に対して係数を掛けるProcessorです。

floatVector2Vector3型に対応したものが存在します。

Vector2、Vector3型に関しては、それぞれの要素毎に個別の係数を指定できます。

Scale系のProcessorは、一部のデバイスの入力感度を個別に変えたい場合などに重宝します。

Axis deadzone、Stick deadzone

ゲームパッドのジョイスティックの中には、ニュートラル位置にあるにもかかわらず0以外の入力が得られてしまうドリフトが発生する)ことがあります。

この問題は、センサの取り得る値の範囲を決めて、ソフトウェア的に遊びを設けることで解消できます。

1軸入力版のAxis deadzone、2軸入力版のStick deadzoneがあります。

Operand TypeはAxis deadzoneがfloat、Stick deadzoneがVector2です。

より詳細な使い方や挙動を理解したい方は、以下記事をご覧ください。

Processorを自作する

ここまで紹介したProcessorは、Input Systemパッケージ側で用意されているものですが、独自のProcessorを自作して適用することも可能です。

自作の流れは次のようになります。

自作の流れ
  • InputProcessor<T>継承クラスをスクリプトで定義
  • 初期化時、InputSystemにProcessorを登録する処理を実装
  • Processorの処理を実装

これらの手順は、以下公式リファレンスに記載されていますが、いくつか注意すべき点があります。

参考:Processors | Input System | 1.3.0

具体例を示しながら実装方法を解説します。

完成スクリプトの実装例

最初に、自作Processorの完成形の例を示します。

Vector2DValueShiftProcessor.cs
using UnityEngine;
using UnityEngine.InputSystem;

#if UNITY_EDITOR
using UnityEditor;
#endif

#if UNITY_EDITOR
[InitializeOnLoad]
#endif
public class Vector2DValueShiftProcessor : InputProcessor<Vector2>
{
    // publicでなければプロパティに表示されない
    // Vector2のような型はエラー、プリミティブ型かEnum型のフィールドである必要がある
    public float shiftX;
    public float shiftY;

    private const string ProcessorName = "Vector2DValueShift";

#if UNITY_EDITOR
    static Vector2DValueShiftProcessor() => Initialize();
#endif

    // Processorの登録処理
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void Initialize()
    {
        // 重複登録すると、Input ActionのProcessor一覧に正しく表示されない事があるため、
        // 重複チェックを行う
        if (InputSystem.TryGetProcessor(ProcessorName) == null)
            InputSystem.RegisterProcessor<Vector2DValueShiftProcessor>(ProcessorName);
    }

    // 独自のProcessorの処理定義
    public override Vector2 Process(Vector2 value, InputControl control)
    {
        return value + new Vector2(shiftX, shiftY);
    }
}

上記スクリプトをVector2DValueShiftProcessor.csなどの名前でUnityプロジェクトに保存すれば、新しいProcessorとして機能するようになります。

スクリプトの解説

例で示したサンプルは、Vector2型の入力値を加工する自作Processorを実装したものです。

独自のProcessorは、次のコードのように、InputSystem<T>クラスを継承して実装します。

public class Vector2DValueShiftProcessor : InputProcessor<Vector2>

InputProcessor<T>はProcessorを表す基底クラスです。

参考:Class InputProcessor<TValue>| Input System | 1.3.0

Processorのプロパティとして編集可能なパラメータは、publicフィールドとして定義する必要があります。

// publicでなければプロパティに表示されない
// Vector2のような型はエラー、プリミティブ型かEnum型のフィールドである必要がある
public float shiftX;
public float shiftY;

実際に値を加工する処理は、以下の部分となります。

// 独自のProcessorの処理定義
public override Vector2 Process(Vector2 value, InputControl control)
{
    return value + new Vector2(shiftX, shiftY);
}

InputSystemへのProcessorの登録は、次の処理で行っています。

// Processorの登録処理
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void Initialize()
{
    // 重複登録すると、Input ActionのProcessor一覧に正しく表示されない事があるため、
    // 重複チェックを行う
    if (InputSystem.TryGetProcessor(ProcessorName) == null)
        InputSystem.RegisterProcessor<Vector2DValueShiftProcessor>(ProcessorName);
}

これらの処理は公式リファレンスに則っていますが、一部処理を変更しています。理由は、Processorの重複登録が行われるとProcessorメニューの選択項目に表示されなくなる現象が発生するためです。(後述します)

自作Processorにエディタ拡張を適用する

次のようなスクリプトをEditorフォルダ配下に置くことで適用できます。

#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using UnityEngine.InputSystem.Editor;

public class Vector2ValueShiftProcessorEditor : InputParameterEditor<Vector2DValueShiftProcessor>
{
    private GUIContent m_SliderLabelX = new GUIContent("Shift X By");
    private GUIContent m_SliderLabelY = new GUIContent("Shift Y By");

    // protectedにする必要あり
    protected override void OnEnable()
    {
    }

    public override void OnGUI()
    {
        target.shiftX = EditorGUILayout.Slider(m_SliderLabelX, target.shiftX, -10, 10);
        target.shiftY = EditorGUILayout.Slider(m_SliderLabelY, target.shiftY, -10, 10);
    }
}
#endif

この例の場合、以下のようなスライダーの見た目に変更されます。

自作Processorを実装する際の注意点

Processorを独自実装するにあたり、いくつか嵌った点がありましたので、軽くまとめます。

パラメータはpublic、かつプリミティブ型かEnum型のみ

通常のシリアライザのようにprivateフィールドに[SerializeField]属性を付けても有効化されません。

また、プリミティブ型(int、floatなど)かEnum型でなければならないという制約があるため、Vector2型などそれ以外の型をフィールドとして持たせてしまうと次のようなエラーが出てしまいます。

ArgumentException: Don't know how to convert PrimitiveValue to 'Object'
Parameter name: type

Processorの重複登録を行うと、メニューに表示されなくなる

以下のような形で登録処理を実装すると、UnityEditorから再生したとき等にProcessorの重複登録が発生してしまうことがあります。

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void Initialize()
{
    InputSystem.RegisterProcessor<Vector2DValueShiftProcessor>(ProcessorName);
}

この問題は、以下Issueでも報告されています。

参考:New Input System processors not appearing always – Unity Forum

Input System 1.3.0でもこの問題が発生するため、本記事ではやむを得ず重複チェックを行うことで回避しています。

もしメニューに表示されなくなった場合は、UnityEditorを再起動するか、Processorを定義しているスクリプトを再インポートし直せば復活する場合があります。

InputParameterEditor.OnEnable()メソッドはprotected

公式リファレンスのエディタ拡張サンプルでは、アクセス修飾子がpublicになっていますが、実装上はprotectedが正解です。publicのままではエラーが出てしまう状況です。

スクリプトリファレンスでもprotectedとなっているので、こちらに合わせると問題なく動くようになります。

参考:Class InputParameterEditor<TObject>| Input System | 1.3.0

さいごに

Input SystemのProcessorは、入力値に対して様々な加工処理を施すことが出来る機能です。

独自のProcessorを適用することで、入力に関わる処理とそれ以外を切り分けることが出来、役割を綺麗に分けることが出来るメリットがあります。

Input Systemの強力な機能の一つなので、活用方法を検討してみてください。

参考サイト

スポンサーリンク