白黒羊

【Unity New Input System】カスタムコンポジットを作成したときにテストでエラーが出るようになる

前提: カスタムコンポジットとは

New Input System は入力と出力の Binding (バインディング/結びつけ) をするための機能です。

一般的には、ひとつの入力に対応するひとつの出力を結びつけていくのですが、複数の入力をまとめて一体的に動作させたいこともあります。例えば、キーボードのWASDキーを使って、マウス操作やゲームパッドのスティックに相当する2次元コントロール (2D Vector) を構成したい、という状況です。
これを可能にするのがComposite (コンポジット) という考え方です。そのほか、「←」キーと「→」キーを使って、マウススクロールに相当する1次元軸(1D Axis) を形成することもできます。

上記のようにいくつかの Composite はすでに定義されていますが、これでは足りないという場合は自分で定義することもできます。

https://docs.unity3d.com/Packages/com.unity.inputsystem@1.3/manual/ActionBindings.html#writing-custom-composites

新しい Composite を定義するには、InputBindingComposite をベースにしたクラスを作成します。
以下の例では InputBindingComposite を継承している Vector2Composite を継承して modifier 付きの 2D Vector 操作を作ってみました。

using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Composites;
using UnityEngine.InputSystem.Layouts;
using UnityEngine.InputSystem.Utilities;

namespace Project.Scripts.Utility
{
#if UNITY_EDITOR
    [InitializeOnLoad]
#endif
    [DisplayStringFormat("{up}/{left}/{down}/{right}+{modifier}")]
    public class KeyboardWithOneModifierComposite : Vector2Composite
    {
#if UNITY_EDITOR
        static KeyboardWithOneModifierComposite()
        {
            Initialize();
        }
#endif

        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
        public static void Initialize()
        {
            InputSystem.RegisterBindingComposite<KeyboardWithOneModifierComposite>();
        }

        [UsedImplicitly] [InputControl(layout = "Modifier")]
        public int Modifier;

        public override Vector2 ReadValue(ref InputBindingCompositeContext context)
        {
            return context.ReadValueAsButton(Modifier)
                ? base.ReadValue(ref context)
                : Vector2.zero;
        }
    }
}

Unit Test中に生じたエラー

ここからが本題です。
この機能を確認するために PlayMode でテストを実行したところ、テストが失敗し、以下のようなエラーメッセージが表示されました。

InvalidOperationException while resolving binding ‘PageChange:KeyboardWithOneModifier’ in action map ‘InputActions (UnityEngine.InputSystem.InputActionAsset):UI’
UnityEngine.InputSystem.InputActionAsset:Enable ()
Project.Scripts.Controller.Common.InputActions:Enable () (at Assets/Project/Scripts/Controller/Common/InputActions.cs:984)
Project.Scripts.Controller.Common.InputActions:.cctor () (at Assets/Project/Scripts/Controller/Common/PartialInputActions.cs:12)
Project.Tests.Runtime.Controller.GridSignalPublisherTest/TestBehaviour:Awake () (at Assets/Project/Tests/Runtime/Controller/GridSignalPublisherTest.cs:95)
UnityEngine.TestTools.MonoBehaviourTest`1:.ctor (bool)
……

テストでは、素のInputActionsを使うのでカスタムコンポジットが追加されていません。その中でカスタムコンポジットを使おうとすることによるエラーでした。

解決するためには、PlayModeテストの中の TestBehaviour#Awake() 内などで毎回、以下のように初期化してあげることで使いたいカスタムコンポジットを導入できます。

KeyboardWithOneModifierComposite.Initialize();

参考

https://forum.unity.com/threads/custom-composite-binding-in-package-breaks-test-in-the-project-that-uses-it.859699/