diff --git a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs index f2e9cb174a..08865b9417 100644 --- a/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs +++ b/Assets/Tests/InputSystem.Editor/CustomProcessorEnumTest.cs @@ -8,6 +8,7 @@ using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Editor; +using UnityEngine.InputSystem.Processors; using UnityEngine.TestTools; using UnityEngine.UIElements; @@ -108,5 +109,69 @@ public IEnumerator ProcessorEnum_ShouldSerializeByValue_WhenSerializedToAsset() yield return null; } + + [Test] + [Description("Regression test for case ISXB-1674")] + public void Migration_ShouldProduceValidActionAsset_WithEnumProcessorConverted() + { + const string k_Json = @" + { + ""name"": ""InputSystem_Actions"", + ""maps"": [ + { + ""name"": ""Player"", + ""id"": ""df70fa95-8a34-4494-b137-73ab6b9c7d37"", + ""actions"": [ + { + ""name"": ""Move"", + ""type"": ""Value"", + ""id"": ""938a78a0-f8c6-4b2e-b8a7-d3c26d83a4e9"", + ""expectedControlType"": ""Vector2"", + ""processors"": ""StickDeadzone,InvertVector2(invertX=false),Custom(SomeEnum=1)"", + ""interactions"": """", + ""initialStateCheck"": true + } + ], + ""bindings"": [ + { + ""name"": """", + ""id"": ""c9a175a0-a5ed-4e2c-b3a9-1d4d3d3a7a9a"", + ""path"": ""/leftStick"", + ""interactions"": """", + ""processors"": """", + ""groups"": """", + ""action"": ""Move"", + ""isComposite"": false, + ""isPartOfComposite"": false + } + ] + } + ], + ""controlSchemes"": [] + }"; + + var asset = InputActionAsset.FromJson(k_Json); + + // Enable the action to call rebinding + asset.FindAction("Move").Enable(); + + var map = asset.FindActionMap("Player"); + + // Directly tests the outcome of the migration and parsing logic, which is the core goal. + var instantiatedProcessors = map.m_State.processors; + Assert.That(map.m_State.totalProcessorCount, Is.EqualTo(3), "Should create exactly three processors."); + + // Verify the order and type of each processor. + Assert.That(instantiatedProcessors[0], Is.TypeOf(), "First processor should be StickDeadzone."); + Assert.That(instantiatedProcessors[1], Is.TypeOf(), "Second processor should be InvertVector2."); + Assert.That(instantiatedProcessors[2], Is.TypeOf(), "Third processor should be the custom type."); + + // Verify the specific data for processors with parameters. + var invertProcessor = (InvertVector2Processor)instantiatedProcessors[1]; + Assert.That(invertProcessor.invertX, Is.False, "invertX parameter should be false."); + + var customProcessor = (CustomProcessor)instantiatedProcessors[2]; + Assert.That(customProcessor.SomeEnum, Is.EqualTo(SomeEnum.OptionB), "Enum parameter should be parsed correctly to OptionB."); + } } #endif diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 40687b93e0..94055e4757 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -33,6 +33,7 @@ however, it has to be formatted properly to pass verification tests. - Fixed HID parsing not handling logical minimum and maximum values correctly when they are negative. This applies for platforms that parse HID descriptors in the package, e.g. macOS at the moment. - Fix usage of correct data format for stick axes in HID Layout Builder ([User contribution](https://github.com/Unity-Technologies/InputSystem/pull/2245)) - Fixed InputSystemUIInputModule calling pointer events on parent objects even when the "Send Pointer Hover To Parent" is off on 2022.3.XX. This was was previously only available on Unity 6 versions since 1.11.0. [ISXB-1296] +- Fixed upgrading input actions containing multiple processors [ISXB-1674] ## [1.15.0] - 2025-10-03 diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs index 3b9a70fcca..e655a6a712 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs @@ -1016,6 +1016,11 @@ internal void MigrateJson(ref ReadFileJson parsedJson) return; if ((parsedJson.maps?.Length ?? 0) > 0 && (parsedJson.version) < JsonVersion.Version1) { + List parsedList = null; + var converted = new List(8); + var updatedParameters = new List(4); + var enumValuesCache = new Dictionary(8); + for (var mi = 0; mi < parsedJson.maps.Length; ++mi) { var mapJson = parsedJson.maps[mi]; @@ -1026,44 +1031,49 @@ internal void MigrateJson(ref ReadFileJson parsedJson) if (string.IsNullOrEmpty(raw)) continue; - var list = NameAndParameters.ParseMultiple(raw).ToList(); - var rebuilt = new List(list.Count); - foreach (var nap in list) + if (!NameAndParameters.ParseMultiple(raw, ref parsedList)) + continue; + + converted.Clear(); + + for (int i = 0; i < parsedList.Count; ++i) { + var nap = parsedList[i]; var procType = InputSystem.TryGetProcessor(nap.name); if (nap.parameters.Count == 0 || procType == null) { - rebuilt.Add(nap.ToString()); + converted.Add(nap); continue; } - - var dict = nap.parameters.ToDictionary(p => p.name, p => p.value.ToString()); - var anyChanged = false; - foreach (var field in procType.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(f => f.FieldType.IsEnum)) + updatedParameters.Clear(); + for (int k = 0; k < nap.parameters.Count; ++k) { - if (dict.TryGetValue(field.Name, out var ordS) && int.TryParse(ordS, out var ord)) + var param = nap.parameters[k]; + var updatedPar = param; + + var fieldInfo = procType.GetField(param.name, BindingFlags.Public | BindingFlags.Instance); + if (fieldInfo != null && fieldInfo.FieldType.IsEnum) { - var values = Enum.GetValues(field.FieldType).Cast().ToArray(); - if (ord >= 0 && ord < values.Length) + var index = param.value.ToInt32(); + if (index >= 0) { - dict[field.Name] = Convert.ToInt32(values[ord]).ToString(); - anyChanged = true; + if (!enumValuesCache.TryGetValue(fieldInfo.FieldType, out var values)) + { + values = Enum.GetValues(fieldInfo.FieldType); + enumValuesCache[fieldInfo.FieldType] = values; + } + if (index < values.Length) + { + var convertedValue = Convert.ToInt32(values.GetValue(index)); + updatedPar = NamedValue.From(param.name, convertedValue); + } } } + updatedParameters.Add(updatedPar); } - - if (!anyChanged) - { - rebuilt.Add(nap.ToString()); - } - else - { - var paramText = string.Join(",", dict.Select(kv => $"{kv.Key}={kv.Value}")); - rebuilt.Add($"{nap.name}({paramText})"); - } + converted.Add(NameAndParameters.Create(nap.name, updatedParameters)); } - - actionJson.processors = string.Join(";", rebuilt); + actionJson.processors = NameAndParameters.ToSerializableString(converted); mapJson.actions[ai] = actionJson; } parsedJson.maps[mi] = mapJson; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs index fd51f4d26e..6c253c46b4 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs @@ -37,7 +37,7 @@ public void OnAddElement(string name) var newElement = new NameAndParameters() { name = name}; interactionsOrProcessorsList.Add(newElement); - m_ListProperty.stringValue = ToSerializableString(interactionsOrProcessorsList); + m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList); m_ListProperty.serializedObject.ApplyModifiedProperties(); } @@ -61,7 +61,7 @@ private void SwapElement(int oldIndex, int newIndex) if (interactionsOrProcessors.Length == 0 || !newIndexIsValid || !oldIndexIsValid) return; MemoryHelpers.Swap(ref interactionsOrProcessors[oldIndex], ref interactionsOrProcessors[newIndex]); - m_ListProperty.stringValue = ToSerializableString(interactionsOrProcessors); + m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessors); m_ListProperty.serializedObject.ApplyModifiedProperties(); } @@ -69,7 +69,7 @@ private void DeleteElement(int index) { var interactionsOrProcessorsList = NameAndParameters.ParseMultiple(m_ListProperty.stringValue).ToList(); interactionsOrProcessorsList.RemoveAt(index); - m_ListProperty.stringValue = ToSerializableString(interactionsOrProcessorsList); + m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList); m_ListProperty.serializedObject.ApplyModifiedProperties(); } @@ -77,19 +77,10 @@ private void OnParametersChanged(ParameterListView listView, int index) { var interactionsOrProcessorsList = NameAndParameters.ParseMultiple(m_ListProperty.stringValue).ToList(); interactionsOrProcessorsList[index] = new NameAndParameters { name = interactionsOrProcessorsList[index].name, parameters = listView.GetParameters() }; - m_ListProperty.stringValue = ToSerializableString(interactionsOrProcessorsList); + m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList); m_ListProperty.serializedObject.ApplyModifiedProperties(); } - private static string ToSerializableString(IEnumerable parametersForEachListItem) - { - if (parametersForEachListItem == null) - return string.Empty; - - return string.Join(NamedValue.Separator, - parametersForEachListItem.Select(x => x.ToString()).ToArray()); - } - public override void RedrawUI(InputActionsEditorState state) { if (m_ContentContainer != null) diff --git a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs index 15ceed089c..b4491e8eec 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Utilities/NameAndParameters.cs @@ -27,6 +27,23 @@ public override string ToString() return $"{name}({parameterString})"; } + internal static string ToSerializableString(IEnumerable list) + { + if (list == null) + return string.Empty; + + return string.Join(NamedValue.Separator, list.Select(x => x.ToString()).ToArray()); + } + + internal static NameAndParameters Create(string name, IList parameters) + { + return new NameAndParameters + { + name = name, + parameters = new ReadOnlyArray(parameters.ToArray()) + }; + } + public static IEnumerable ParseMultiple(string text) { List list = null;