げんとめブログ

NewInputSystemにデバイスを追加する

参考元:

新しくデバイスを追加する

PS4のコントローラーを追加する


NewInputSystemの入力元として、アセットとかで買ったサードパーティの入力APIを追加したいときについてです。

LeanTouchをNewInputSystemのタッチ入力として使うつもりで実装していきます。

Touchだけなら既に実装されていますが。


とりあえずコード

using System.Collections;
using System.Collections.Generic;
using Lean.Touch;
using UnityEditor;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem.Utilities;

#if UNITY_EDITOR
[InitializeOnLoad]
#endif
[InputControlLayout(displayName = "LeanTouch",stateType = typeof(LeanTouchState))]
[UnityEngine.Scripting.Preserve]
public class InputFromLeanTouch : InputDevice,IInputUpdateCallbackReceiver
{
    public static InputFromLeanTouch current { get; private set; }
    public static IReadOnlyList<InputFromLeanTouch> all => s_AllMyDevices;
    private static List<InputFromLeanTouch> s_AllMyDevices = new List<InputFromLeanTouch>();
    public ButtonControl button
    {
        get; private set;
    }
    public Vector2Control axis { get; private set; }

    
    protected override void FinishSetup()
    {
        base.FinishSetup();
        //引数のpath==構造体でInputControl属性のnameに一致するものが返り値になる
        button = GetChildControl<ButtonControl>("button");//buttonにアクセスしやすいように
        axis = GetChildControl<Vector2Control>("axis1");//axis1にアクセスしやすいように
        
    }
    /// <summary>
    /// IInputUpdateCallbackReceiverの実装するべきメソッド
    /// 今回のように、入力用のAPI(LeanTouch)が既にある場合
    /// このメソッド内で値の更新をする
    /// </summary>
    public void OnUpdate()
    {
        var state = new LeanTouchState();

        state.axis = LeanGesture.GetScreenDelta();
        if (LeanTouch.Fingers.Count > 0)
        {
            state.buttons |= 1 << 0;
        }
        InputSystem.QueueStateEvent(this,state);
    }

    public override void MakeCurrent()
    {
        base.MakeCurrent();
        current = this;
    }

    protected override void OnAdded()
    {
        base.OnAdded();
        s_AllMyDevices.Add(this);
    }

    protected override void OnRemoved()
    {
        base.OnRemoved();
        s_AllMyDevices.Remove(this);
    }

    static InputFromLeanTouch()
    {
        InitializeInPlayer();
    }

    [RuntimeInitializeOnLoadMethod]
    static void InitializeInPlayer()
    {
        //このメソッドでInputActionアセットのBindingPathに反映される
        InputSystem.RegisterLayout<InputFromLeanTouch>("LeanTouch");
        
        //デバイスを追加するには下のメソッドを呼び出す
        //InputSystem.AddDevice<InputFromLeanTouch>();
        //今回の場合はLeanTouchがシーン内に置いてある必要があるので実行時にAwakeから呼び出す
        //そのためのコンポーネントを別に作る必要があるので
        //LeanTouchSupport.csを別に作る
    }
}

//デバイスから取得する情報を構造体として定義する
//InputActionアセットで使うところのControlTypeとかバインドに使う名前
//値の型とか
public struct LeanTouchState : IInputStateTypeInfo
{
    //何でもよいみたいです
    //FourCCという識別する仕組みがあるみたい?
    public FourCC format => new FourCC('L', 'T', 'T', 'O');
//InputControl属性
//nameはInputActionアセットのBinding➡Pathに表示される文字列
//bitは対応しているbitの列  ビットフラグでボタン状態の管理をしている
//layoutはInputActionのControlTypeに対応しているもの、だと思います
//  layoutを省略した場合、フィールドの型から自動でそれなりに判別してくれるそう
    [InputControl(name = "button",layout = "Button",bit = 0)]
    public ushort buttons;//ushortは16bitなのでボタン16個分管理できる
    
    [InputControl(name = "axis1",layout = "Vector2")]
    //下のように書くと、axis1のXとYのそれぞれの軸について細かく設定できる模様
    //何も書かなくてもaxis1.xとyは自動で追加されています
    //細かい設定の引数については公式のドキュメントを
    [InputControl(name = "axis1/x", defaultState = 0.0f, format = "FLT", parameters = "normalize,normalizeMin=-1,normalizeMax=1,normalizeZero=0.0,clamp=2,clampMin=-1,clampMax=1")]
    [InputControl(name = "axis1/y", defaultState = 0.0f, format = "FLT", parameters = "normalize,normalizeMin=-1,normalizeMax=1,normalizeZero=0.0,clamp=2,clampMin=-1,clampMax=1")]
    public Vector2 axis;
}

次のファイルはLeanTouchを持ったオブジェクトと一緒につけます。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Layouts;

/// 
public class LeanTouchSupport : MonoBehaviour
{
    private void Awake()
    {
        //このWithInterfaceというのがイマイチ理解できませんでした
        //デバイスで使っているAPI(LeanTouch)と、自分が作ったデバイスを追加するスクリプト(InputFromLeanTouch)を
        //対応させるために付けているのかも
        //デバイスが削除されたとき(LeanTouchのオブジェクトが消されたときとか)に対応しているデバイスをまとめて削除するために
        //付けているのかも
        InputSystem.RegisterLayout(
            matches:new InputDeviceMatcher().WithInterface("LeanTouch"));    }

    private void OnEnable()
    {
        //これでInputActionアセットを使って入力を取得することができる
        InputSystem.AddDevice(
            new InputDeviceDescription()
            {
                interfaceName = "LeanTouch",
                product = name
            });
    }

    private void OnDisable()
    {
        var device = InputSystem.devices.FirstOrDefault(
            x => x.description == new InputDeviceDescription()
            {
                interfaceName = "LeanTouch",
                product = name
            });
        if (device != null)
        {
            InputSystem.RemoveDevice(device);
        }
    }
}

これで何とか最低限の実装は出来ました。値の設定については適当です。OnUpdate内を必要なように実装する必要があります。

ステップとしては

  1. 入力を受け取るデータを「構造体」として定義する
  2. InputActionアセットで表示する情報をInputSystem.RegisterLayout()を使って実装する
  3. InputSystem.AddDevice()を使って使うデバイスをInputSystemに登録する

のような感じになりました。

今回は新しくデバイスを追加しましたが、レイアウトを追加するだけなら別にやり方があるかもしれません。Touchのデバイス自体は標準で用意されているので、それに追加する感じで実装できたらもっと楽にできるのかも。


NewInputSystem自体の使い方はすでにいくつか情報がありますので省略しました。


別にそのままLeanTouchを使っても良いんですが、AndroidだけじゃなくSteamでも公開したいなと思っていたので、コントローラの差を吸収してくれるNewInputSystemを使ってみようと思いました。

InputActionを介して入力処理を実装すればとりあえず入力自体はInputActionアセットを変更するだけでよくなります。

理解できればそこまで大変ではないかもしれません。でも面倒は面倒です。使いまわせるように出来たら良いんですが。

Exit mobile version