200 lines
9.8 KiB
C#
200 lines
9.8 KiB
C#
#if ENABLE_INPUT_SYSTEM
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using Invector.vItemManager;
|
|
using NUnit.Framework;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityEngine.InputSystem;
|
|
using UnityEngine.InputSystem.LowLevel;
|
|
using UnityEngine.InputSystem.UI;
|
|
|
|
namespace Invector.vCharacterController.InputSystemEditor
|
|
{
|
|
public class vInputSystemRegressionTests
|
|
{
|
|
private const string Root = "Assets/Invector-3rdPersonController/";
|
|
private static readonly FieldInfo ActionField = typeof(GenericInput).GetField("inputAction", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
private static readonly FieldInfo InstanceField = typeof(vInput).GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic);
|
|
private readonly List<Object> created = new List<Object>();
|
|
private InputActionAsset actions;
|
|
private Gamepad pad;
|
|
private Keyboard keyboard;
|
|
private object previousInstance;
|
|
private object fixture;
|
|
|
|
[SetUp]
|
|
public void Setup()
|
|
{
|
|
// The predefined editor assembly cannot reference the package's test-only asmdef directly.
|
|
var fixtureType = System.Type.GetType("UnityEngine.InputSystem.InputTestFixture, Unity.InputSystem.TestFramework", true);
|
|
fixture = System.Activator.CreateInstance(fixtureType);
|
|
fixtureType.GetMethod("Setup").Invoke(fixture, null);
|
|
previousInstance = InstanceField.GetValue(null);
|
|
var manager = Track(new GameObject("Input regression manager"));
|
|
InstanceField.SetValue(null, manager.AddComponent<vInput>());
|
|
pad = InputSystem.AddDevice<Gamepad>();
|
|
keyboard = InputSystem.AddDevice<Keyboard>();
|
|
actions = Track(InputActionAsset.FromJson(System.IO.File.ReadAllText(Root + "Basic Locomotion/InputSystem/InvectorPlayerInputs.inputactions")));
|
|
actions.Enable();
|
|
}
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
try
|
|
{
|
|
foreach (var obj in created.AsEnumerable().Reverse())
|
|
if (obj) Object.DestroyImmediate(obj);
|
|
created.Clear();
|
|
InstanceField.SetValue(null, previousInstance);
|
|
}
|
|
finally { if (fixture != null) fixture.GetType().GetMethod("TearDown").Invoke(fixture, null); }
|
|
}
|
|
|
|
private T Track<T>(T obj) where T : Object
|
|
{
|
|
created.Add(obj);
|
|
return obj;
|
|
}
|
|
|
|
private void Bind(GenericInput input)
|
|
{
|
|
if (input.inputActionReference == null) return;
|
|
var reference = Track(InputActionReference.Create(actions.FindAction(input.inputActionReference.action.id)));
|
|
ActionField.SetValue(input, reference);
|
|
input.GetButtonDown();
|
|
}
|
|
|
|
private vInventory LoadInventory(string name)
|
|
{
|
|
var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(Root + "ItemManager/Prefabs/" + name + ".prefab");
|
|
var root = Track(Object.Instantiate(prefab));
|
|
root.SetActive(false);
|
|
var inventory = root.GetComponentInChildren<vInventory>(true);
|
|
inventory.canEquip = true;
|
|
foreach (var control in inventory.changeEquipmentControllers)
|
|
{
|
|
control.useItemInput.useInput = false;
|
|
Bind(control.previousItemInput);
|
|
Bind(control.nextItemInput);
|
|
control.equipArea.onChangeEquipSlot = new OnChangeEquipmentEvent();
|
|
control.equipArea.onEquipItem = new OnChangeEquipmentEvent();
|
|
control.equipArea.onUnequipItem = new OnChangeEquipmentEvent();
|
|
}
|
|
foreach (var selector in root.GetComponentsInChildren<vControlAreaByInput>(true))
|
|
{
|
|
selector.inventory = inventory;
|
|
foreach (var slot in selector.slotsSelectors) Bind(slot.input);
|
|
}
|
|
return inventory;
|
|
}
|
|
|
|
private static void Tick(vInventory inventory)
|
|
{
|
|
foreach (var selector in inventory.GetComponentsInChildren<vControlAreaByInput>(true))
|
|
typeof(vControlAreaByInput).GetMethod("Update", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(selector, null);
|
|
typeof(vInventory).GetMethod("ChangeEquipmentInput", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(inventory, null);
|
|
}
|
|
|
|
[Test]
|
|
public void EquipmentDirectionsCycleOnlyTheirArea(
|
|
[Values("Inventory_ShooterMelee", "Inventory_MeleeOnly")] string prefab,
|
|
[Values(false, true)] bool gamepad,
|
|
[Values(0, 1, 2)] int area)
|
|
{
|
|
var inventory = LoadInventory(prefab);
|
|
var target = inventory.changeEquipmentControllers[area].equipArea;
|
|
var validSlots = Enumerable.Range(0, target.equipSlots.Count).Where(i => target.equipSlots[i].isValid).ToArray();
|
|
Assert.That(validSlots.Length, Is.GreaterThan(1));
|
|
foreach (var control in inventory.changeEquipmentControllers) control.equipArea.indexOfEquippedItem = 0;
|
|
var directions = new[] { GamepadButton.DpadRight, GamepadButton.DpadLeft, GamepadButton.DpadDown };
|
|
var keys = new[] { Key.RightArrow, Key.LeftArrow, Key.DownArrow };
|
|
for (int press = 0; press < validSlots.Length + 1; press++)
|
|
{
|
|
InputSystem.QueueStateEvent(pad, new GamepadState());
|
|
InputSystem.QueueStateEvent(keyboard, new KeyboardState());
|
|
InputSystem.Update();
|
|
Tick(inventory);
|
|
if (gamepad) InputSystem.QueueStateEvent(pad, new GamepadState().WithButton(directions[area]));
|
|
else InputSystem.QueueStateEvent(keyboard, new KeyboardState(keys[area]));
|
|
InputSystem.Update();
|
|
Tick(inventory);
|
|
int expected = validSlots[(press + 1) % validSlots.Length];
|
|
Assert.That(target.indexOfEquippedItem, Is.EqualTo(expected), "Each press must advance exactly one available slot.");
|
|
foreach (var other in inventory.changeEquipmentControllers.Where((c, i) => i != area))
|
|
Assert.That(other.equipArea.indexOfEquippedItem, Is.Zero, "Another equipment area consumed the same input.");
|
|
InputSystem.Update();
|
|
Tick(inventory);
|
|
Assert.That(target.indexOfEquippedItem, Is.EqualTo(expected), "Holding a direction must not select another slot.");
|
|
}
|
|
}
|
|
|
|
[TestCase(-1f, 1f, 0f)]
|
|
[TestCase(0f, 0f, 0f)]
|
|
[TestCase(1f, 0f, 1f)]
|
|
public void LockOnUsesRightStickHorizontal(float x, float previous, float next)
|
|
{
|
|
InputSystem.QueueStateEvent(pad, new GamepadState { rightStick = new Vector2(x, 0f) });
|
|
InputSystem.Update();
|
|
Assert.That(actions.FindAction("Previous Target").ReadValue<float>(), Is.EqualTo(previous));
|
|
Assert.That(actions.FindAction("Next Target").ReadValue<float>(), Is.EqualTo(next));
|
|
}
|
|
|
|
[TestCase("Previous Target", "Z")]
|
|
[TestCase("Next Target", "X")]
|
|
public void LockOnAxisFiresOncePerDeflectionAndKeepsKeyboard(string actionName, string key)
|
|
{
|
|
var input = JsonUtility.FromJson<GenericInput>("{\"useInput\":true,\"inputSource\":1,\"inputSystemAxis\":true,\"inputSystemRawThreshold\":0.1}");
|
|
ActionField.SetValue(input, Track(InputActionReference.Create(actions.FindAction(actionName))));
|
|
float x = actionName == "Previous Target" ? -1f : 1f;
|
|
Assert.That(input.GetButtonDown(), Is.False);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
InputSystem.QueueStateEvent(pad, new GamepadState { rightStick = new Vector2(x, 0f) });
|
|
InputSystem.Update();
|
|
Assert.That(input.GetButtonDown(), Is.True);
|
|
InputSystem.Update();
|
|
Assert.That(input.GetButtonDown(), Is.False);
|
|
InputSystem.QueueStateEvent(pad, new GamepadState());
|
|
InputSystem.Update();
|
|
Assert.That(input.GetButtonDown(), Is.False);
|
|
}
|
|
InputSystem.QueueStateEvent(keyboard, new KeyboardState((Key)System.Enum.Parse(typeof(Key), key)));
|
|
InputSystem.Update();
|
|
Assert.That(input.GetButtonDown(), Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void ShooterOnlyRetainsItsAuthoredDirectSlotShortcuts()
|
|
{
|
|
var inventory = LoadInventory("Inventory_ShooterOnly");
|
|
foreach (var selector in inventory.GetComponentsInChildren<vControlAreaByInput>(true))
|
|
foreach (var slot in selector.slotsSelectors)
|
|
{
|
|
Assert.That(slot.input.useInput, Is.True);
|
|
Assert.That(slot.input.inputActionReference, Is.Not.Null);
|
|
Assert.That(slot.input.inputActionReference.action.bindings.Any(b => b.path.StartsWith("<Gamepad>/dpad/")), Is.True);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void EventSystemSelectsOnlyNewBackendAndPreservesLegacyConfiguration()
|
|
{
|
|
var root = Track(new GameObject("UI backend regression", typeof(EventSystem), typeof(StandaloneInputModule)));
|
|
var legacy = root.GetComponent<StandaloneInputModule>();
|
|
legacy.horizontalAxis = "CustomHorizontal";
|
|
var backend = root.AddComponent<vEventSystemInputBackend>();
|
|
backend.Configure();
|
|
backend.Configure();
|
|
Assert.That(legacy.enabled, Is.False);
|
|
Assert.That(legacy.horizontalAxis, Is.EqualTo("CustomHorizontal"));
|
|
Assert.That(root.GetComponents<InputSystemUIInputModule>().Length, Is.EqualTo(1));
|
|
Assert.That(root.GetComponent<InputSystemUIInputModule>().enabled, Is.True);
|
|
}
|
|
}
|
|
}
|
|
#endif
|