From 3258450d5843bcb44a14c9b9244c7cdb84f3f0a2 Mon Sep 17 00:00:00 2001 From: Paulius Dervinis <54306142+Pauliusd01@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:24:57 +0300 Subject: [PATCH 01/14] Bump develop version to 1.8.0-pre.1 (#1732) * Bump version to 1.8.0-pre --- Assets/Samples/InGameHints/InGameHintsActions.cs | 2 +- Assets/Samples/SimpleDemo/SimpleControls.cs | 2 +- Assets/Tests/InputSystem/InputActionCodeGeneratorActions.cs | 2 +- .../InputActionCodeGeneratorActions.inputactions.meta | 2 +- Packages/com.unity.inputsystem/CHANGELOG.md | 2 ++ Packages/com.unity.inputsystem/InputSystem/AssemblyInfo.cs | 4 ++-- .../InputSystem/Devices/Precompiled/FastKeyboard.cs | 2 +- .../InputSystem/Devices/Precompiled/FastMouse.cs | 2 +- .../InputSystem/Devices/Precompiled/FastTouchscreen.cs | 2 +- .../com.unity.inputsystem/Tests/TestFixture/AssemblyInfo.cs | 2 +- Packages/com.unity.inputsystem/package.json | 2 +- 11 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Assets/Samples/InGameHints/InGameHintsActions.cs b/Assets/Samples/InGameHints/InGameHintsActions.cs index 69dd7b0cde..27c8d642f9 100644 --- a/Assets/Samples/InGameHints/InGameHintsActions.cs +++ b/Assets/Samples/InGameHints/InGameHintsActions.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was auto-generated by com.unity.inputsystem:InputActionCodeGenerator -// version 1.7.0 +// version 1.8.0 // from Assets/Samples/InGameHints/InGameHintsActions.inputactions // // Changes to this file may cause incorrect behavior and will be lost if diff --git a/Assets/Samples/SimpleDemo/SimpleControls.cs b/Assets/Samples/SimpleDemo/SimpleControls.cs index 8cc949c6ee..6d78258ea7 100644 --- a/Assets/Samples/SimpleDemo/SimpleControls.cs +++ b/Assets/Samples/SimpleDemo/SimpleControls.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was auto-generated by com.unity.inputsystem:InputActionCodeGenerator -// version 1.7.0 +// version 1.8.0 // from Assets/Samples/SimpleDemo/SimpleControls.inputactions // // Changes to this file may cause incorrect behavior and will be lost if diff --git a/Assets/Tests/InputSystem/InputActionCodeGeneratorActions.cs b/Assets/Tests/InputSystem/InputActionCodeGeneratorActions.cs index bf78a3f705..e09c1824ce 100644 --- a/Assets/Tests/InputSystem/InputActionCodeGeneratorActions.cs +++ b/Assets/Tests/InputSystem/InputActionCodeGeneratorActions.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was auto-generated by com.unity.inputsystem:InputActionCodeGenerator -// version 1.7.0 +// version 1.8.0 // from Assets/Tests/InputSystem/InputActionCodeGeneratorActions.inputactions // // Changes to this file may cause incorrect behavior and will be lost if diff --git a/Assets/Tests/InputSystem/InputActionCodeGeneratorActions.inputactions.meta b/Assets/Tests/InputSystem/InputActionCodeGeneratorActions.inputactions.meta index a2cbcc5628..3b44e51dfd 100644 --- a/Assets/Tests/InputSystem/InputActionCodeGeneratorActions.inputactions.meta +++ b/Assets/Tests/InputSystem/InputActionCodeGeneratorActions.inputactions.meta @@ -8,7 +8,7 @@ ScriptedImporter: assetBundleName: assetBundleVariant: script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3} - generateWrapperCode: 0 + generateWrapperCode: 1 wrapperCodePath: wrapperClassName: wrapperCodeNamespace: diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index cf47b0fdb8..004aebad1c 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. Due to package verification, the latest version below is the unpublished version and the date is meaningless. however, it has to be formatted properly to pass verification tests. +## [Unreleased] + ## [1.7.0] - 2023-08-14 ### Added diff --git a/Packages/com.unity.inputsystem/InputSystem/AssemblyInfo.cs b/Packages/com.unity.inputsystem/InputSystem/AssemblyInfo.cs index 1f1f45767b..cda40f010b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/AssemblyInfo.cs +++ b/Packages/com.unity.inputsystem/InputSystem/AssemblyInfo.cs @@ -15,7 +15,7 @@ public static partial class InputSystem // Keep this in sync with "Packages/com.unity.inputsystem/package.json". // NOTE: Unfortunately, System.Version doesn't use semantic versioning so we can't include // "-preview" suffixes here. - internal const string kAssemblyVersion = "1.7.0"; - internal const string kDocUrl = "https://docs.unity3d.com/Packages/com.unity.inputsystem@1.7"; + internal const string kAssemblyVersion = "1.8.0"; + internal const string kDocUrl = "https://docs.unity3d.com/Packages/com.unity.inputsystem@1.8"; } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastKeyboard.cs b/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastKeyboard.cs index bbd94e2467..4c75cb447e 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastKeyboard.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastKeyboard.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was auto-generated by com.unity.inputsystem:InputLayoutCodeGenerator -// version 1.7.0 +// version 1.8.0 // from "Keyboard" layout // // Changes to this file may cause incorrect behavior and will be lost if diff --git a/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastMouse.cs b/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastMouse.cs index 5a59181b3e..72cd3999b7 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastMouse.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastMouse.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was auto-generated by com.unity.inputsystem:InputLayoutCodeGenerator -// version 1.7.0 +// version 1.8.0 // from "Mouse" layout // // Changes to this file may cause incorrect behavior and will be lost if diff --git a/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastTouchscreen.cs b/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastTouchscreen.cs index 6d0f6ca270..be4e6f3043 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastTouchscreen.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Devices/Precompiled/FastTouchscreen.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was auto-generated by com.unity.inputsystem:InputLayoutCodeGenerator -// version 1.7.0 +// version 1.8.0 // from "Touchscreen" layout // // Changes to this file may cause incorrect behavior and will be lost if diff --git a/Packages/com.unity.inputsystem/Tests/TestFixture/AssemblyInfo.cs b/Packages/com.unity.inputsystem/Tests/TestFixture/AssemblyInfo.cs index 55ebaa2595..b0820402a2 100644 --- a/Packages/com.unity.inputsystem/Tests/TestFixture/AssemblyInfo.cs +++ b/Packages/com.unity.inputsystem/Tests/TestFixture/AssemblyInfo.cs @@ -4,7 +4,7 @@ // Keep this in sync with "Packages/com.unity.inputsystem/package.json". // NOTE: Unfortunately, System.Version doesn't use semantic versioning so we can't include // "-preview" suffixes here. -[assembly: AssemblyVersion("1.7.0")] +[assembly: AssemblyVersion("1.8.0")] [assembly: InternalsVisibleTo("Unity.InputSystem.Tests.Editor")] [assembly: InternalsVisibleTo("Unity.InputSystem.Tests")] [assembly: InternalsVisibleTo("Unity.InputSystem.IntegrationTests")] diff --git a/Packages/com.unity.inputsystem/package.json b/Packages/com.unity.inputsystem/package.json index 8428aa9ce8..10f2e80a81 100755 --- a/Packages/com.unity.inputsystem/package.json +++ b/Packages/com.unity.inputsystem/package.json @@ -1,7 +1,7 @@ { "name": "com.unity.inputsystem", "displayName": "Input System", - "version": "1.7.0", + "version": "1.8.0-pre.1", "unity": "2019.4", "description": "A new input system which can be used as a more extensible and customizable alternative to Unity's classic input system in UnityEngine.Input.", "keywords": [ From bb2b10935e4eaccc8fde872fc7d9ae552b508d50 Mon Sep 17 00:00:00 2001 From: Rita Merkl <127492464+ritamerkl@users.noreply.github.com> Date: Wed, 16 Aug 2023 15:43:46 +0200 Subject: [PATCH 02/14] NEW: Only show matching binding-paths for selected control scheme (ISX-1549) (#1724) * hide other not matching bindings if control scheme selected * fixed selected control scheme does not reset issue --- .../Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs | 2 +- .../Editor/UITKAssetEditor/Views/BindingPropertiesView.cs | 3 +++ .../Views/CompositePartBindingPropertiesView.cs | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs index fa02be7bdc..f0682542eb 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs @@ -80,7 +80,7 @@ public static Command SelectControlScheme(int controlSchemeIndex) return (in InputActionsEditorState state) => { if (controlSchemeIndex == -1) - return state.With(selectedControlSchemeIndex: controlSchemeIndex); + return state.With(selectedControlSchemeIndex: controlSchemeIndex, selectedControlScheme: new InputControlScheme()); var controlSchemeSerializedProperty = state.serializedObject .FindProperty(nameof(InputActionAsset.m_ControlSchemes)) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs index 688b098fab..1c58ea11a4 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs @@ -23,6 +23,7 @@ public BindingPropertiesView(VisualElement root, Foldout foldout, StateContainer (_, controlSchemes, s) => new ViewState { controlSchemes = controlSchemes, + currentControlScheme = s.selectedControlScheme, selectedBinding = Selectors.GetSelectedBinding(s), selectedBindingIndex = s.selectedBindingIndex, selectedBindingPath = Selectors.GetSelectedBindingPath(s), @@ -57,6 +58,7 @@ public override void RedrawUI(ViewState viewState) { var controlPathEditor = new InputControlPathEditor(viewState.selectedBindingPath, new InputControlPickerState(), () => { Dispatch(Commands.ApplyModifiedProperties()); }); + controlPathEditor.SetControlPathsToMatch(viewState.currentControlScheme.deviceRequirements.Select(x => x.controlPath)); var inputAction = viewState.selectedInputAction; controlPathEditor.SetExpectedControlLayout(inputAction?.expectedControlType ?? ""); @@ -100,6 +102,7 @@ internal class ViewState public int selectedBindingIndex; public SerializedInputBinding? selectedBinding; public ViewStateCollection controlSchemes; + public InputControlScheme currentControlScheme; public SerializedProperty selectedBindingPath; public SerializedInputAction? selectedInputAction; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositePartBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositePartBindingPropertiesView.cs index ea225d66d9..a71a0fa18c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositePartBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositePartBindingPropertiesView.cs @@ -40,6 +40,7 @@ public override void RedrawUI(ViewState viewState) var controlPathEditor = new InputControlPathEditor(viewState.selectedBindingPath, new InputControlPickerState(), () => { Dispatch(Commands.ApplyModifiedProperties()); }); + controlPathEditor.SetControlPathsToMatch(viewState.currentControlScheme.deviceRequirements.Select(x => x.controlPath)); controlPathEditor.SetExpectedControlLayout(viewState.expectedControlLayoutName); m_PathEditorContainer.onGUIHandler = controlPathEditor.OnGUI; @@ -59,6 +60,7 @@ internal class ViewState public SerializedProperty selectedBindingPath; public SerializedInputBinding selectedBinding; public IEnumerable compositePartNames; + public InputControlScheme currentControlScheme; public string expectedControlLayoutName; public string selectedCompositePartName; } @@ -78,6 +80,7 @@ public static CompositePartBindingPropertiesView.ViewState GetCompositePartBindi selectedBinding = binding, selectedBindingPath = GetSelectedBindingPath(state), selectedCompositePartName = selectedCompositePartName, + currentControlScheme = state.selectedControlScheme, compositePartNames = compositeParts.Select(ObjectNames.NicifyVariableName).ToList(), expectedControlLayoutName = InputBindingComposite.GetExpectedControlLayoutName(binding.compositePath, binding.name) ?? "" }; From 6da7853f82ec9bd4613022a77d21da548d6ebc3c Mon Sep 17 00:00:00 2001 From: Rita Merkl <127492464+ritamerkl@users.noreply.github.com> Date: Wed, 16 Aug 2023 17:18:18 +0200 Subject: [PATCH 03/14] FIX: bindings presentation for control schemes (ISX-1132) (#1725) * hide/show bindings for selected control schemes * fix for {GLOBAL} identifier * PR fixes on names & explanatory comments --- .../UITKAssetEditor/Views/ActionsTreeView.cs | 49 +++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs index f636b20cb9..efd9121e44 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs @@ -291,6 +291,7 @@ public static List> GetActionsAsTreeViewDa var actionMapIndex = state.selectedActionMapIndex; var actionMaps = state.serializedObject.FindProperty(nameof(InputActionAsset.m_ActionMaps)); + var controlSchemes = state.serializedObject.FindProperty(nameof(InputActionAsset.m_ControlSchemes)); var actionMap = actionMapIndex == -1 || actionMaps.arraySize <= 0 ? null : actionMaps.GetArrayElementAtIndex(actionMapIndex); @@ -323,10 +324,14 @@ public static List> GetActionsAsTreeViewDa var nextBinding = actionBindings[++i]; while (nextBinding.isPartOfComposite) { - var name = GetHumanReadableCompositeName(nextBinding, state.selectedControlScheme, state.selectedControlSchemeIndex); - - compositeItems.Add(new TreeViewItemData(id++, - new ActionOrBindingData(false, name, actionMapIndex, false, GetControlLayout(nextBinding.path), nextBinding.indexOfBinding))); + var isVisible = ShouldBindingBeVisible(nextBinding, state.selectedControlScheme); + if (isVisible) + { + var name = GetHumanReadableCompositeName(nextBinding, state.selectedControlScheme, controlSchemes); + compositeItems.Add(new TreeViewItemData(id++, + new ActionOrBindingData(false, name, actionMapIndex, false, + GetControlLayout(nextBinding.path), nextBinding.indexOfBinding))); + } if (++i >= actionBindings.Count) break; @@ -340,9 +345,11 @@ public static List> GetActionsAsTreeViewDa } else { - bindingItems.Add(new TreeViewItemData(id++, - new ActionOrBindingData(false, GetHumanReadableBindingName(serializedInputBinding, state.selectedControlSchemeIndex, state.selectedControlScheme), actionMapIndex, - false, GetControlLayout(serializedInputBinding.path), serializedInputBinding.indexOfBinding))); + var isVisible = ShouldBindingBeVisible(serializedInputBinding, state.selectedControlScheme); + if (isVisible) + bindingItems.Add(new TreeViewItemData(id++, + new ActionOrBindingData(false, GetHumanReadableBindingName(serializedInputBinding, state.selectedControlScheme, controlSchemes), actionMapIndex, + false, GetControlLayout(serializedInputBinding.path), serializedInputBinding.indexOfBinding))); } } actionItems.Add(new TreeViewItemData(id++, @@ -351,28 +358,42 @@ public static List> GetActionsAsTreeViewDa return actionItems; } - private static string GetHumanReadableBindingName(SerializedInputBinding serializedInputBinding, int currentControlSchemeIndex, InputControlScheme? currentControlScheme) + private static string GetHumanReadableBindingName(SerializedInputBinding serializedInputBinding, InputControlScheme? currentControlScheme, SerializedProperty allControlSchemes) { var name = InputControlPath.ToHumanReadableString(serializedInputBinding.path); if (String.IsNullOrEmpty(name)) name = ""; - if (!IsBindingPartOfCurrentControlScheme(serializedInputBinding, currentControlScheme, currentControlSchemeIndex)) + if (IsBindingAssignedToNoControlSchemes(serializedInputBinding, allControlSchemes, currentControlScheme)) name += " {GLOBAL}"; return name; } - private static bool IsBindingPartOfCurrentControlScheme(SerializedInputBinding serializedInputBinding, InputControlScheme? currentControlScheme, int currentControlSchemeIndex) + private static bool IsBindingAssignedToNoControlSchemes(SerializedInputBinding serializedInputBinding, SerializedProperty allControlSchemes, InputControlScheme? currentControlScheme) { - if (currentControlScheme.HasValue && currentControlSchemeIndex >= 0) - return serializedInputBinding.controlSchemes.Contains(currentControlScheme.Value.name); + if (allControlSchemes.arraySize <= 0 || !currentControlScheme.HasValue || string.IsNullOrEmpty(currentControlScheme.Value.name)) + return false; + if (serializedInputBinding.controlSchemes.Length <= 0) + return true; + return false; + } + private static bool ShouldBindingBeVisible(SerializedInputBinding serializedInputBinding, InputControlScheme? currentControlScheme) + { + if (currentControlScheme.HasValue && !string.IsNullOrEmpty(currentControlScheme.Value.name)) + { + //if binding is global (not assigned to any control scheme) show always + if (serializedInputBinding.controlSchemes.Length <= 0) + return true; + return serializedInputBinding.controlSchemes.Contains(currentControlScheme.Value.name); + } + //if no control scheme selected then show all bindings return true; } - internal static string GetHumanReadableCompositeName(SerializedInputBinding binding, InputControlScheme? currentControlScheme, int currentControlSchemeIndex) + internal static string GetHumanReadableCompositeName(SerializedInputBinding binding, InputControlScheme? currentControlScheme, SerializedProperty allControlSchemes) { return $"{ObjectNames.NicifyVariableName(binding.name)}: " + - $"{GetHumanReadableBindingName(binding, currentControlSchemeIndex, currentControlScheme)}"; + $"{GetHumanReadableBindingName(binding, currentControlScheme, allControlSchemes)}"; } private static string GetControlLayout(string path) From fda8b4b60483ffdef6fc52defc70f74df922b1f5 Mon Sep 17 00:00:00 2001 From: Rita Merkl <127492464+ritamerkl@users.noreply.github.com> Date: Thu, 17 Aug 2023 09:18:13 +0200 Subject: [PATCH 04/14] ISX-1514 multiple bindings created on single add button click fix (#1728) --- .../InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs index efd9121e44..0cfd57ac90 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs @@ -49,6 +49,7 @@ public ActionsTreeView(VisualElement root, StateContainer stateContainer) if (item.isAction) { addBindingButton.style.display = DisplayStyle.Flex; + addBindingButton.clickable = null; //reset the clickable to avoid multiple subscriptions addBindingButton.clicked += () => AddBinding(item.name); treeViewItem.EditTextFinishedCallback = newName => { From b19428ef15f39a5a9b6d524339974f5106e7ec8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= Date: Thu, 17 Aug 2023 11:25:52 +0200 Subject: [PATCH 05/14] FIX: Remove errors when using UITK Asset Editor (ISX-1531) (#1723) This avoids a breaking API change for the InputParameterEditor class. Developers need to override this method to implement a custom UI for UITK Asset Editor. --- .../InputSystem/Editor/InputParameterEditor.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/InputParameterEditor.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/InputParameterEditor.cs index 0350e8c9ce..dbe05472ef 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/InputParameterEditor.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/InputParameterEditor.cs @@ -182,6 +182,17 @@ internal override void SetTarget(object target) OnEnable(); } +#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR + /// + /// Default stub implementation of . + /// Should be overridden to create the desired UI. + /// + public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) + { + } + +#endif + /// /// Helper for parameters that have defaults (usually from ). /// From 02e8b036a85a7345d615285414119189b1a14875 Mon Sep 17 00:00:00 2001 From: Rita Merkl <127492464+ritamerkl@users.noreply.github.com> Date: Thu, 17 Aug 2023 15:18:06 +0200 Subject: [PATCH 06/14] FIX: save and reset input action asset (ISX-1525, ISX-1483, ISX-1559) (#1727) * work on copy of input actions asset (reset not working fix) * dirty input actions editor window if changes were made * show dialog on close input actions editor (WIP) * fixed cancel closing input asset editor * avoid changing asset name to (Clone) in the name * fixed saving logic for multiple open assets * only change header if needed * PR refactor - reload asset from json instead of cloning it * fixed reopening window after Cancel --- .../InputActionsEditorWindow.cs | 83 +++++++++++++++++-- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs index e76064f195..ef2f08cfd6 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs @@ -25,6 +25,9 @@ internal class InputActionsEditorWindow : EditorWindow { private static readonly string k_FileExtension = "." + InputActionAsset.Extension; private int m_AssetId; + private string m_AssetPath; + private string m_AssetJson; + private bool m_IsDirty; [OnOpenAsset] public static bool OpenAsset(int instanceId, int line) @@ -51,6 +54,7 @@ public static bool OpenAsset(int instanceId, int line) window.Focus(); return true; } + window.m_IsDirty = false; window.m_AssetId = instanceId; window.titleContent = new GUIContent("Input Actions Editor"); window.SetAsset(asset); @@ -74,8 +78,10 @@ private static InputActionsEditorWindow GetOrCreateWindow(int id, out bool isAlr private void SetAsset(InputActionAsset asset) { + m_AssetPath = AssetDatabase.GetAssetPath(asset); var serializedAsset = new SerializedObject(asset); m_State = new InputActionsEditorState(serializedAsset); + m_AssetJson = File.ReadAllText(m_AssetPath); bool isGUIDObtained = AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out m_AssetGUID, out long _); Debug.Assert(isGUIDObtained, $"Failed to get asset {asset.name} GUID"); @@ -95,6 +101,8 @@ private void CreateGUI() if (m_State.serializedObject == null) { var asset = GetAssetFromDatabase(); + m_AssetPath = AssetDatabase.GetAssetPath(asset); + m_AssetJson = File.ReadAllText(m_AssetPath); var serializedAsset = new SerializedObject(asset); m_State = new InputActionsEditorState(m_State, serializedAsset); } @@ -119,10 +127,70 @@ private void BuildUI() private void OnStateChanged(InputActionsEditorState newState) { + DirtyInputActionsEditorWindow(newState); if (InputEditorUserSettings.autoSaveInputActionAssets) SaveAsset(m_State.serializedObject); } + private void DirtyInputActionsEditorWindow(InputActionsEditorState newState) + { + var isWindowDirty = !InputEditorUserSettings.autoSaveInputActionAssets && HasAssetChanged(newState.serializedObject); + if (m_IsDirty == isWindowDirty) + return; + m_IsDirty = isWindowDirty; + titleContent = m_IsDirty ? new GUIContent("(*) Input Actions Editor") : new GUIContent("Input Actions Editor"); + } + + private bool HasAssetChanged(SerializedObject serializedAsset) + { + var asset = (InputActionAsset)serializedAsset.targetObject; + var newAssetJson = asset.ToJson(); + return newAssetJson != m_AssetJson; + } + + private void OnDestroy() + { + ConfirmSaveChangesIfNeeded(); + } + + private void ConfirmSaveChangesIfNeeded() + { + // Do we have unsaved changes? + if (!m_IsDirty) + return; + + var result = EditorUtility.DisplayDialogComplex("Input Action Asset has been modified", $"Do you want to save the changes you made in:\n{m_AssetPath}\n\nYour changes will be lost if you don't save them.", "Save", "Cancel", "Don't Save"); + switch (result) + { + case 0: // Save + SaveAsset(m_State.serializedObject, this); + break; + case 1: // Cancel editor quit. (open new editor window with the edited asset) + ReshowEditorWindowWithUnsavedChanges(); + break; + case 2: // Don't save, quit - reload the old asset from the json to prevent the asset from being dirtied + AssetDatabase.ImportAsset(m_AssetPath); + break; + } + } + + private void ReshowEditorWindowWithUnsavedChanges() + { + var window = CreateWindow(); + CopyOldStatsToNewWindow(window); + window.BuildUI(); + window.Show(); + } + + private void CopyOldStatsToNewWindow(InputActionsEditorWindow window) + { + window.m_AssetId = m_AssetId; + window.m_State = m_State; + window.m_AssetPath = m_AssetPath; + window.m_AssetJson = m_AssetJson; + window.m_IsDirty = true; + } + private InputActionAsset GetAssetFromDatabase() { Debug.Assert(!string.IsNullOrEmpty(m_AssetGUID), "Asset GUID is empty"); @@ -133,18 +201,21 @@ private InputActionAsset GetAssetFromDatabase() [SerializeField] private InputActionsEditorState m_State; [SerializeField] private string m_AssetGUID; - public static void SaveAsset(SerializedObject serializedAsset) + public static void SaveAsset(SerializedObject serializedAsset, InputActionsEditorWindow currentWindow = null) { + if ((focusedWindow == null || focusedWindow is not InputActionsEditorWindow) && currentWindow == null) + return; + currentWindow = currentWindow ? currentWindow : (InputActionsEditorWindow)focusedWindow; var asset = (InputActionAsset)serializedAsset.targetObject; - var assetPath = AssetDatabase.GetAssetPath(asset); var assetJson = asset.ToJson(); - var existingJson = File.ReadAllText(assetPath); + var existingJson = File.ReadAllText(currentWindow.m_AssetPath); if (assetJson != existingJson) { - EditorHelpers.CheckOut(assetPath); - File.WriteAllText(assetPath, assetJson); - AssetDatabase.ImportAsset(assetPath); + EditorHelpers.CheckOut(currentWindow.m_AssetPath); + File.WriteAllText(currentWindow.m_AssetPath, assetJson); + AssetDatabase.ImportAsset(currentWindow.m_AssetPath); + currentWindow.m_AssetJson = assetJson; } } } From 1a5f8fbffb029e91dcb8733c13f6a2facf378e22 Mon Sep 17 00:00:00 2001 From: Rita Merkl <127492464+ritamerkl@users.noreply.github.com> Date: Thu, 17 Aug 2023 18:27:47 +0200 Subject: [PATCH 07/14] setting min-size to avoid invisible bindings properties view (#1734) --- .../UITKAssetEditor/Resources/InputActionsEditorStyles.uss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditorStyles.uss b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditorStyles.uss index 3603042efa..12dce47ad4 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditorStyles.uss +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditorStyles.uss @@ -7,7 +7,7 @@ } .body-panel-container { - min-width: auto; + min-width: 150px; border-top-width: 1px; border-left-color: rgb(25, 25, 25); border-right-color: rgb(25, 25, 25); From d426234b1f2f4440dff4911b33a1e1095d5d382a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= Date: Tue, 22 Aug 2023 11:03:48 +0200 Subject: [PATCH 08/14] CHANGE: Enable IMGUI Asset Editor for input action assets when UITK is available (ISX-1541) (#1731) The UITK Editor view is only enabled for the Project Wide Actions Asset Editor, in the Project Settings. --- .../Editor/AssetEditor/InputActionEditorWindow.cs | 3 +-- .../Editor/UITKAssetEditor/InputActionsEditorWindow.cs | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionEditorWindow.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionEditorWindow.cs index 068c5c1138..09ac63ad88 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionEditorWindow.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionEditorWindow.cs @@ -38,10 +38,9 @@ internal class InputActionEditorWindow : EditorWindow, IDisposable public static bool OnOpenAsset(int instanceId, int line) { #if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR - if (InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseUIToolkitEditor)) + if (InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseUIToolkitEditor) && InputActionsEditorWindow.isWindowEnabled) return false; #endif - var path = AssetDatabase.GetAssetPath(instanceId); if (!path.EndsWith(k_FileExtension, StringComparison.InvariantCultureIgnoreCase)) return false; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs index ef2f08cfd6..8e7351c3a9 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs @@ -24,6 +24,13 @@ static EnableUITKEditor() internal class InputActionsEditorWindow : EditorWindow { private static readonly string k_FileExtension = "." + InputActionAsset.Extension; + /// + /// Controls whether the UITK version of the InputActionAsset Editor is enabled or not for editing Input Action + /// assets. + /// + /// At the moment, the UITK Asset Editor doesn't have feature parity with the IMGUI version. + /// This is set to false to show the IMGUI version of the InputActionAsset Editor instead. + internal static bool isWindowEnabled = false; private int m_AssetId; private string m_AssetPath; private string m_AssetJson; @@ -32,7 +39,7 @@ internal class InputActionsEditorWindow : EditorWindow [OnOpenAsset] public static bool OpenAsset(int instanceId, int line) { - if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseUIToolkitEditor)) + if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseUIToolkitEditor) || !isWindowEnabled) return false; var path = AssetDatabase.GetAssetPath(instanceId); From a56878c491f4b185bc161f7d83ebdb9a53678cb9 Mon Sep 17 00:00:00 2001 From: James McGill Date: Tue, 29 Aug 2023 12:05:29 +0200 Subject: [PATCH 09/14] NEW: Project-wide Actions (#1735) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add InputSystem.actions property * Add ProjectWideActions Test Sample * Rename conditonal compilation flag for project-wide actions * Ensure UITK action editor appears in Project Settings * Fix project settings editor not populating actions * Fix for dupliate editors shown * FIX: Global Actions UI flickers when user expands actions (#1738) * Fix for API validation test failure * Fix ProjectWide Actions Enable/Disable flow for tests * Prevent assertion which may happen if ProjectWide Actions are Enabled without any devices attached yet in tests * Disable ProjectWide actions in some tests that assume there are no other actions * Fixed issue with Input Actions UI becoming blank due to error (ISX-1584) * DOCF-3823 - pre release docs * Added stub doc to InputSystem.actions * Added compilation constant to enable global actions during docs generation * Fixed whitespace typo * Update PreReleaseNotes.md tidied whitespace in code sample * Updated define for documentation generation to include the project wide actions API * Hiding search field while not implemented (ISX-1582) * Updated control bindings based on latest review Next/Previous updated to be more intuative/same axis Names for UI Click* restored to original Added keyboard binding for Attack Added some XR bindings Some reordering too E.g. so crouch/jump next to each other This makes the diff slightly harder * Fix tests not compiling on older editors * Ran formatting script to fix warnings * Hiding save, auto save buttons in project wide input settings as its auto saving always atm (ISX-1556) * Fixed delete key mapping to be 'delete' rather than 'space' (ISX-1552) * Fix ActionState and StateBuffer going out of sync when entering tests * Disable ProjectWide actions in some tests that assume there are no other actions or controls * Temporarily disable some tests which have issues when ProjectWide Actions are enabled * fix formatting * Update InputSystem.cs removed duplicate comment on private member * Fix broken links * improve api docs for InputSystem.actions * add changelog entry --------- Co-authored-by: João Co-authored-by: Lyndon Homewood Co-authored-by: Ben Pitt --- .../CustomComposite/CustomComposite.cs | 2 +- Assets/Samples/ProjectWideActionsTest.meta | 8 + .../ProjectWideActionsTest.asmdef | 22 + .../ProjectWideActionsTest.asmdef.meta | 7 + .../ProjectWideActionsTest.cs | 66 ++ .../ProjectWideActionsTest.cs.meta | 11 + .../ProjectWideActionsTest.unity | 461 ++++++++ .../ProjectWideActionsTest.unity.meta | 7 + .../ControlSchemeEditorTests.cs | 2 +- .../InputSystem.Editor/SelectorsTests.cs | 2 +- Assets/Tests/InputSystem.Editor/TestData.cs | 2 +- .../InputSystem.Editor/TestDataGenerators.cs | 2 +- Assets/Tests/InputSystem/CoreTests_Actions.cs | 81 ++ .../CoreTests_Actions_Interactions.cs | 5 + .../Tests/InputSystem/CoreTests_Controls.cs | 6 + Assets/Tests/InputSystem/CoreTests_Devices.cs | 23 + Assets/Tests/InputSystem/CoreTests_Editor.cs | 11 + Assets/Tests/InputSystem/Plugins/UITests.cs | 8 + .../Unity.InputSystem.Tests.asmdef | 5 + Packages/com.unity.inputsystem/CHANGELOG.md | 3 + .../Images/ProjectSettingsInputActions.png | Bin 0 -> 98794 bytes .../Documentation~/PreReleaseNotes.md | 92 ++ .../Documentation~/TableOfContents.md | 1 + .../Documentation~/config.json | 1 + .../Actions/Composites/Vector2Composite.cs | 2 +- .../Actions/Composites/Vector3Composite.cs | 2 +- .../InputSystem/Actions/InputControlScheme.cs | 2 +- .../Actions/Interactions/HoldInteraction.cs | 2 +- .../Interactions/MultiTapInteraction.cs | 2 +- .../Actions/Interactions/PressInteraction.cs | 2 +- .../Interactions/SlowTapInteraction.cs | 2 +- .../Actions/Interactions/TapInteraction.cs | 2 +- .../Processors/AxisDeadzoneProcessor.cs | 2 +- .../Processors/StickDeadzoneProcessor.cs | 2 +- .../AssetEditor/InputActionEditorWindow.cs | 4 +- .../Editor/AssetEditor/ParameterListView.cs | 2 +- .../Editor/InputParameterEditor.cs | 8 +- .../Editor/ProjectWideActions.meta | 8 + .../ProjectWideActionsAsset.cs | 99 ++ .../ProjectWideActionsAsset.cs.meta | 11 + .../ProjectWideActionsTemplate.inputactions | 1017 +++++++++++++++++ ...ojectWideActionsTemplate.inputactions.meta | 14 + .../Settings/InputSettingsBuildProvider.cs | 41 +- .../Editor/Settings/InputSettingsProvider.cs | 1 + .../UITKAssetEditor/Commands/Commands.cs | 6 +- .../Commands/ControlSchemeCommands.cs | 2 +- .../InputActionsEditorSettingsProvider.cs | 31 +- .../InputActionsEditorState.cs | 2 +- .../InputActionsEditorWindow.cs | 41 +- .../InputActionsEditorWindowUtils.cs | 35 + .../InputActionsEditorWindowUtils.cs.meta | 11 + .../Resources/InputActionsEditor.uxml | 5 +- .../Resources/InputActionsEditorStyles.uss | 9 + .../UITKAssetEditor/SerializedInputAction.cs | 2 +- .../UITKAssetEditor/SerializedInputBinding.cs | 2 +- .../Editor/UITKAssetEditor/StateContainer.cs | 3 +- .../UITKAssetEditor/Views/ActionMapsView.cs | 2 +- .../Views/ActionPropertiesView.cs | 2 +- .../UITKAssetEditor/Views/ActionsTreeView.cs | 25 +- .../Views/BindingPropertiesView.cs | 2 +- .../Views/CompositeBindingPropertiesView.cs | 2 +- .../CompositePartBindingPropertiesView.cs | 2 +- .../UITKAssetEditor/Views/ContextMenu.cs | 2 +- .../Views/ControlSchemesView.cs | 2 +- .../Views/InputActionViewsControlsHolder.cs | 2 +- .../Views/InputActionsEditorView.cs | 2 +- .../Views/InputActionsTreeViewItem.cs | 2 +- .../Views/NameAndParametersListView.cs | 2 +- .../UITKAssetEditor/Views/PropertiesView.cs | 2 +- .../Editor/UITKAssetEditor/Views/Selectors.cs | 2 +- .../Editor/UITKAssetEditor/Views/ViewBase.cs | 2 +- .../Views/VisualElementExtensions.cs | 2 +- .../InputSystem/InputFeatureNames.cs | 4 +- .../InputSystem/InputManagerStateMonitors.cs | 2 +- .../InputSystem/InputSystem.cs | 85 +- .../InputSystem/Unity.InputSystem.asmdef | 6 + 76 files changed, 2231 insertions(+), 123 deletions(-) create mode 100644 Assets/Samples/ProjectWideActionsTest.meta create mode 100644 Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.asmdef create mode 100644 Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.asmdef.meta create mode 100644 Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.cs create mode 100644 Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.cs.meta create mode 100644 Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.unity create mode 100644 Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.unity.meta create mode 100644 Packages/com.unity.inputsystem/Documentation~/Images/ProjectSettingsInputActions.png create mode 100644 Packages/com.unity.inputsystem/Documentation~/PreReleaseNotes.md create mode 100644 Packages/com.unity.inputsystem/Documentation~/config.json create mode 100644 Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions.meta create mode 100644 Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs.meta create mode 100644 Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions create mode 100644 Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions.meta create mode 100644 Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindowUtils.cs create mode 100644 Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindowUtils.cs.meta diff --git a/Assets/Samples/CustomComposite/CustomComposite.cs b/Assets/Samples/CustomComposite/CustomComposite.cs index 6c26fb1db1..003c30acb6 100644 --- a/Assets/Samples/CustomComposite/CustomComposite.cs +++ b/Assets/Samples/CustomComposite/CustomComposite.cs @@ -177,7 +177,7 @@ public override void OnGUI() target.scaleFactor = EditorGUILayout.Slider(m_ScaleFactorLabel, currentValue, 0, 2); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) { var slider = new Slider(m_ScaleFactorLabel.text, 0, 2) diff --git a/Assets/Samples/ProjectWideActionsTest.meta b/Assets/Samples/ProjectWideActionsTest.meta new file mode 100644 index 0000000000..efb99bbd41 --- /dev/null +++ b/Assets/Samples/ProjectWideActionsTest.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bd9dcc5a3e3502b42b13e8ad113ab8f1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.asmdef b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.asmdef new file mode 100644 index 0000000000..e64fd2edc5 --- /dev/null +++ b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.asmdef @@ -0,0 +1,22 @@ +{ + "name": "ProjectWideActionsTest", + "rootNamespace": "", + "references": [ + "GUID:75469ad4d38634e559750d17036d5f7c" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "Unity", + "expression": "2022.3", + "define": "UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.asmdef.meta b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.asmdef.meta new file mode 100644 index 0000000000..9004363bf3 --- /dev/null +++ b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 472aaae9fe1ef1f478743ae4c5ef7592 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.cs b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.cs new file mode 100644 index 0000000000..6bc835bd08 --- /dev/null +++ b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.cs @@ -0,0 +1,66 @@ +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + +using UnityEngine; +using UnityEngine.InputSystem; + +public class NewBehaviourScript : MonoBehaviour +{ + [SerializeField] public GameObject cube; + + InputAction move; + InputAction look; + InputAction attack; + InputAction jump; + InputAction interact; + InputAction next; + InputAction previous; + InputAction sprint; + InputAction crouch; + + // Start is called before the first frame update + void Start() + { + // Project-Wide Actions + move = InputSystem.actions.FindAction("Player/Move"); + look = InputSystem.actions.FindAction("Player/Look"); + attack = InputSystem.actions.FindAction("Player/Attack"); + jump = InputSystem.actions.FindAction("Player/Jump"); + interact = InputSystem.actions.FindAction("Player/Interact"); + next = InputSystem.actions.FindAction("Player/Next"); + previous = InputSystem.actions.FindAction("Player/Previous"); + sprint = InputSystem.actions.FindAction("Player/Sprint"); + crouch = InputSystem.actions.FindAction("Player/Crouch"); + } + + // Update is called once per frame + void Update() + { + if (attack.WasPressedThisFrame()) + { + cube.GetComponent().material.color = Color.red; + } + else if (attack.WasReleasedThisFrame()) + { + cube.GetComponent().material.color = Color.green; + } + + var moveVal = move.ReadValue(); + if (moveVal.x < 0.0f) + { + cube.transform.Translate(new Vector3(-10 * Time.deltaTime, 0, 0)); + } + else if (moveVal.x > 0.0f) + { + cube.transform.Translate(new Vector3(10 * Time.deltaTime, 0, 0)); + } + if (moveVal.y < 0.0f) + { + cube.transform.Translate(new Vector3(0, -10 * Time.deltaTime, 0)); + } + else if (moveVal.y > 0.0f) + { + cube.transform.Translate(new Vector3(0, 10 * Time.deltaTime, 0)); + } + } +} +#endif diff --git a/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.cs.meta b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.cs.meta new file mode 100644 index 0000000000..8b37436419 --- /dev/null +++ b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f28fc6350a3a50a46a9e2448b66b7ab7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.unity b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.unity new file mode 100644 index 0000000000..79b6997161 --- /dev/null +++ b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.unity @@ -0,0 +1,461 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &1227919034 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1227919037} + - component: {fileID: 1227919036} + - component: {fileID: 1227919035} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1227919035 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1227919034} + m_Enabled: 1 +--- !u!20 &1227919036 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1227919034} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1227919037 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1227919034} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1239474184 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1239474186} + - component: {fileID: 1239474185} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1239474185 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1239474184} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1239474186 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1239474184} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1517955871 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1517955873} + m_Layer: 0 + m_Name: Player + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1517955873 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1517955871} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1522618675 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1522618680} + - component: {fileID: 1522618679} + - component: {fileID: 1522618678} + - component: {fileID: 1522618677} + - component: {fileID: 1522618676} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1522618676 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1522618675} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f28fc6350a3a50a46a9e2448b66b7ab7, type: 3} + m_Name: + m_EditorClassIdentifier: + cube: {fileID: 1522618675} +--- !u!65 &1522618677 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1522618675} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 0 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1522618678 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1522618675} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1522618679 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1522618675} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1522618680 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1522618675} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.unity.meta b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.unity.meta new file mode 100644 index 0000000000..834b062901 --- /dev/null +++ b/Assets/Samples/ProjectWideActionsTest/ProjectWideActionsTest.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c9a118a06bb79c0428c11bb701b6756e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/InputSystem.Editor/ControlSchemeEditorTests.cs b/Assets/Tests/InputSystem.Editor/ControlSchemeEditorTests.cs index 25d9905dac..20cdc29736 100644 --- a/Assets/Tests/InputSystem.Editor/ControlSchemeEditorTests.cs +++ b/Assets/Tests/InputSystem.Editor/ControlSchemeEditorTests.cs @@ -1,4 +1,4 @@ -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using NUnit.Framework; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Editor; diff --git a/Assets/Tests/InputSystem.Editor/SelectorsTests.cs b/Assets/Tests/InputSystem.Editor/SelectorsTests.cs index d69dc0e169..c3069fb0af 100644 --- a/Assets/Tests/InputSystem.Editor/SelectorsTests.cs +++ b/Assets/Tests/InputSystem.Editor/SelectorsTests.cs @@ -1,6 +1,6 @@ // UITK TreeView is not supported in earlier versions // Therefore the UITK version of the InputActionAsset Editor is not available on earlier Editor versions either. -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System.Linq; using NUnit.Framework; using UnityEngine; diff --git a/Assets/Tests/InputSystem.Editor/TestData.cs b/Assets/Tests/InputSystem.Editor/TestData.cs index d1a987dc4f..4621e529a2 100644 --- a/Assets/Tests/InputSystem.Editor/TestData.cs +++ b/Assets/Tests/InputSystem.Editor/TestData.cs @@ -1,4 +1,4 @@ -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System.Collections.Generic; using UnityEditor; using UnityEngine; diff --git a/Assets/Tests/InputSystem.Editor/TestDataGenerators.cs b/Assets/Tests/InputSystem.Editor/TestDataGenerators.cs index 1099aea180..ae53e4b49c 100644 --- a/Assets/Tests/InputSystem.Editor/TestDataGenerators.cs +++ b/Assets/Tests/InputSystem.Editor/TestDataGenerators.cs @@ -1,4 +1,4 @@ -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System.Collections.Generic; using UnityEngine.InputSystem; diff --git a/Assets/Tests/InputSystem/CoreTests_Actions.cs b/Assets/Tests/InputSystem/CoreTests_Actions.cs index 5ba4527abb..2932b578ee 100644 --- a/Assets/Tests/InputSystem/CoreTests_Actions.cs +++ b/Assets/Tests/InputSystem/CoreTests_Actions.cs @@ -527,6 +527,11 @@ public void Actions_CanTargetMultipleControls() [Category("Actions")] public void Actions_CanTargetSameControlWithMultipleActions() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); +#endif + var gamepad = InputSystem.AddDevice(); var action1 = new InputAction("action1", binding: "/buttonSouth"); @@ -2042,6 +2047,11 @@ public void Actions_CanCreateActionsWithoutAnActionMap() [Category("Actions")] public void Actions_CanCreateActionAssetWithMultipleActionMaps() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); +#endif + var asset = ScriptableObject.CreateInstance(); var map1 = new InputActionMap("map1"); @@ -2291,6 +2301,11 @@ public void Actions_CanByPassControlActuationChecks_UsingPasshtroughAction() [Category("Actions")] public void Actions_WithMultipleBoundControls_DriveInteractionsFromControlWithGreatestActuation() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); +#endif + var gamepad = InputSystem.AddDevice(); // We go through several permutations of the same behavior all in one test. Makes the @@ -2578,6 +2593,11 @@ public void Actions_WithMultipleBoundControls_ProcessesInteractionsOnAllActiveBi [Category("Actions")] public void Actions_WithMultipleBoundControls_CanHandleInteractionsThatTriggerOnlyOnButtonRelease() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); +#endif + var keyboard = InputSystem.AddDevice(); var gamepad = InputSystem.AddDevice(); @@ -3098,6 +3118,11 @@ public void Actions_CanRecordActions_AndReadValueAsObject() [Category("Actions")] public void Actions_CanRecordAllActionsInTheSystem() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); +#endif + var gamepad = InputSystem.AddDevice(); var map = new InputActionMap(); @@ -3541,6 +3566,11 @@ public void Actions_CanConvertAssetToAndFromJson() [Category("Actions")] public void Actions_CanQueryAllEnabledActions() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); +#endif + var action = new InputAction(binding: "/leftStick"); action.Enable(); @@ -3783,6 +3813,12 @@ private InputActionMap CreateSingletonAction() [TestCaseSource(typeof(ModificationCases))] public void Actions_CanHandleModification(Modification modification, IInputActionCollection2 actions) { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); + InputActionState.DestroyAllActionMapStates(); // Also remove controls so the changed control count is accurate +#endif + var gamepad = InputSystem.AddDevice(); if (modification == Modification.AddDevice || modification == Modification.RemoveDevice) @@ -4669,6 +4705,12 @@ public void Actions_WhenDeviceIsRemoved_ReadingValueInActionListenersWillNotThro [Category("Actions")] public void Actions_WhenDeviceIsRemoved_DeviceIsRemovedFromDeviceMask() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); + InputActionState.DestroyAllActionMapStates(); // Also remove controls so the changed control count is accurate +#endif + var gamepad = InputSystem.AddDevice(); var map = new InputActionMap(); @@ -4811,6 +4853,12 @@ public void Actions_ControlsUpdate_WhenDeviceConfigurationChanges_AndControlIsNo [Category("Actions")] public void Actions_WhenControlsUpdate_NotificationIsTriggered_ButOnlyAfterBindingsHaveFirstBeenResolved() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); + InputActionState.DestroyAllActionMapStates(); // Also remove controls so the changed control count is accurate +#endif + var enabledAction = new InputAction("enabledAction", binding: "/leftTrigger"); // Enabling an action resolves its bindings. From the on, we get notifications for when @@ -4867,6 +4915,12 @@ public void Actions_WhenControlsUpdate_NotificationIsTriggered_ButOnlyAfterBindi [Category("Actions")] public void Actions_WhenControlsUpdateInActionMap_NotificationIsTriggered() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); + InputActionState.DestroyAllActionMapStates(); // Also remove controls so the changed control count is accurate +#endif + var actionMap = new InputActionMap("map"); actionMap.AddAction("action", binding: "/leftTrigger"); actionMap.Enable(); @@ -4893,6 +4947,12 @@ public void Actions_WhenControlsUpdateInActionMap_NotificationIsTriggered() [Category("Actions")] public void Actions_WhenControlsUpdateInActionAsset_NotificationIsTriggered() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); // Worked in Standalone + InputActionState.DestroyAllActionMapStates(); // Also remove controls so the changed control count is accurate +#endif + var asset = ScriptableObject.CreateInstance(); asset.name = "asset"; var actionMap = new InputActionMap("map"); @@ -4960,6 +5020,12 @@ public void Actions_WhenControlsUpdate_TimeoutsAreCarriedOver() [Category("Actions")] public void Actions_WhenControlsUpdate_InProgressActionsKeepGoing() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && UNITY_EDITOR + // @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455) + // Causes: "[Assert] Could not find active control after binding resolution" + // when RemoveDevice() called + InputSystem.actions.Disable(); +#endif currentTime = 0; var gamepad = InputSystem.AddDevice(); @@ -5077,6 +5143,11 @@ public void Actions_WhenInProgress_AddingAndRemovingUnusedDevice_DoesNotAffectAc [Category("Actions")] public void Actions_CanFindEnabledActions() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); +#endif + var action1 = new InputAction(name: "a"); var action2 = new InputAction(name: "b"); @@ -9716,6 +9787,11 @@ public void Actions_CanIterateOverActionsInMap() [Category("Actions")] public void Actions_CanUseTouchWithActions() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); +#endif + var touchscreen = InputSystem.AddDevice(); var primaryTouchAction = new InputAction("PrimaryTouch" , binding: "/primaryTouch/position"); @@ -9786,6 +9862,11 @@ public void Actions_CanUseTouchWithActions() [Category("Actions")] public void Actions_CanDrivePointerInputFromTouchPenAndMouse() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions?.Disable(); +#endif + // Give us known parameters for tap detection. InputSystem.settings.defaultTapTime = 0.5f; InputSystem.settings.tapRadius = 5; diff --git a/Assets/Tests/InputSystem/CoreTests_Actions_Interactions.cs b/Assets/Tests/InputSystem/CoreTests_Actions_Interactions.cs index c76221328a..fc1fd2ccb1 100644 --- a/Assets/Tests/InputSystem/CoreTests_Actions_Interactions.cs +++ b/Assets/Tests/InputSystem/CoreTests_Actions_Interactions.cs @@ -751,6 +751,11 @@ public void Actions_CanPerformDoubleTapInteraction() [Category("Actions")] public void Actions_CanCustomizeButtonPressPointsOfInteractions() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + InputSystem.actions.Disable(); +#endif + var gamepad = InputSystem.AddDevice(); var pressAction = new InputAction("PressAction", binding: "/leftTrigger", interactions: "press(pressPoint=0.234)"); diff --git a/Assets/Tests/InputSystem/CoreTests_Controls.cs b/Assets/Tests/InputSystem/CoreTests_Controls.cs index 5a749ab4db..28d30529b8 100644 --- a/Assets/Tests/InputSystem/CoreTests_Controls.cs +++ b/Assets/Tests/InputSystem/CoreTests_Controls.cs @@ -478,6 +478,12 @@ public unsafe void Controls_ValueIsReadFromStateMemoryOnlyWhenControlHasBeenMark [Category("Controls")] public void Controls_ValueCachingWorksAcrossEntireDeviceMemoryRange() { + // @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455) + // Somehow the presence of action controls prevents those controls being marked as stale +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + InputSystem.actions.Disable(); +#endif + var keyboard = InputSystem.AddDevice(); // read all values to initially mark everything as cached diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 113a128c36..3b6e9ed063 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -4097,6 +4097,13 @@ public void Devices_RemovingDevice_CleansUpUpdateCallback() [Retry(2)] // Warm up JIT public void Devices_RemovingAndReaddingDevice_DoesNotAllocateMemory() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Exclude project-wide actions from this test + // Prevent GC Allocations happening later in test + InputSystem.actions.Disable(); + InputActionState.DestroyAllActionMapStates(); +#endif + var description = new InputDeviceDescription { @@ -4383,6 +4390,12 @@ public void TODO_Devices_CanHijackEventStreamOfDevice() [TestCase(false, InputSettings.BackgroundBehavior.ResetAndDisableNonBackgroundDevices, InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView)] public unsafe void Devices_CanHandleFocusChanges(bool appRunInBackground, InputSettings.BackgroundBehavior backgroundBehavior, InputSettings.EditorInputBehaviorInPlayMode editorInputBehaviorInPlayMode) { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && UNITY_EDITOR + // @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455) + // Causes: "[Assert] Could not find active control after binding resolution" + // due to: mouse3 = InputSystem.AddDevice(); + InputSystem.actions.Disable(); +#endif // The constant leads to "Unreachable code detected" warnings. #pragma warning disable CS0162 @@ -5350,6 +5363,16 @@ public void Devices_RemovingKeyboardMakesNextKeyboardCurrent() [Category("Devices")] public void Devices_RemovingDevice_MakesNextDeviceOfTypeCurrent() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && UNITY_EDITOR + // @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455) + // Causes: "[Assert] Could not find active control after binding resolution" + // during point where Pointer is removed + // - InputActionState.OnDeviceChange(device, InputDeviceChange.Removed); + // - LazyResolveBindings(bool fullResolve) + // - ResolveBindings() + // - RestoreActionStatesAfterReResolvingBindings(UnmanagedMemory oldState, InputControlList activeControls, bool isFullResolve) + InputSystem.actions.Disable(); +#endif var mouse = InputSystem.AddDevice(); Press(mouse.leftButton); Assert.That(Pointer.current, Is.EqualTo(mouse)); diff --git a/Assets/Tests/InputSystem/CoreTests_Editor.cs b/Assets/Tests/InputSystem/CoreTests_Editor.cs index 35147e4c26..7b89605e16 100644 --- a/Assets/Tests/InputSystem/CoreTests_Editor.cs +++ b/Assets/Tests/InputSystem/CoreTests_Editor.cs @@ -2837,6 +2837,17 @@ public void Editor_WhenRunUpdatesInEditModeIsEnabled_InputActionsTriggerInEditMo [Category("Editor")] public void Editor_LeavingPlayMode_DestroysAllActionStates() { +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // With Project-wide Actions `InputSystem.actions`, we begin with some initial ActionState + // Exclude project-wide actions from this test + Assert.That(InputActionState.s_GlobalState.globalList.length, Is.EqualTo(1)); + InputSystem.actions.Disable(); + InputActionState.DestroyAllActionMapStates(); +#endif + + // Initial state + Assert.That(InputActionState.s_GlobalState.globalList.length, Is.EqualTo(0)); + InputSystem.AddDevice(); // Enter play mode. diff --git a/Assets/Tests/InputSystem/Plugins/UITests.cs b/Assets/Tests/InputSystem/Plugins/UITests.cs index 5a4afbcd55..d6365dd3a6 100644 --- a/Assets/Tests/InputSystem/Plugins/UITests.cs +++ b/Assets/Tests/InputSystem/Plugins/UITests.cs @@ -3231,6 +3231,8 @@ public IEnumerator UI_CanPreventAutomaticDeselectionOfGameObjects() Assert.That(scene.eventSystem.currentSelectedGameObject, Is.SameAs(scene.leftGameObject)); } +// @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455) +#if !UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS || UNITY_EDITOR [UnityTest] [Category("UI")] public IEnumerator UI_WhenBindingsAreReResolved_PointerStatesAreKeptInSync() @@ -3281,6 +3283,8 @@ public IEnumerator UI_WhenBindingsAreReResolved_PointerStatesAreKeptInSync() Assert.That(EventSystem.current.IsPointerOverGameObject(), Is.True); } +#endif + ////REVIEW: While `deselectOnBackgroundClick` does solve the problem of breaking keyboard and gamepad navigation, the question //// IMO is whether navigation should even be affected that way by not having a current selection. Seems to me that the //// the system should remember the last selected object and start up navigation from there when nothing is selected. @@ -3796,6 +3800,8 @@ public IEnumerator UI_WhenAppLosesAndRegainsFocus_WhileUIButtonIsPressed_UIButto Assert.That(clicked, Is.True); } +// @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455) +#if !UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS || UNITY_EDITOR [UnityTest] [Category("UI")] public IEnumerator UI_WhenCursorIsLockedToScreenCenter_PointerEnterAndExitEventsFire() @@ -3828,6 +3834,8 @@ public IEnumerator UI_WhenCursorIsLockedToScreenCenter_PointerEnterAndExitEvents Assert.That(callbackReceiver.events.Any(e => e.type == EventType.PointerExit), Is.True); } +#endif + #region Multi Display Tests #if UNITY_2022_3_OR_NEWER // displayIndex is only available from 2022.3 onwards diff --git a/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef b/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef index f7df9c9dce..183d70b1fa 100644 --- a/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef +++ b/Assets/Tests/InputSystem/Unity.InputSystem.Tests.asmdef @@ -40,6 +40,11 @@ "name": "Unity", "expression": "2022.1", "define": "TEMP_DISABLE_STANDALONE_OSX_XINPUT_TEST" + }, + { + "name": "Unity", + "expression": "2022.3", + "define": "UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS" } ], "noEngineReferences": false diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 004aebad1c..6f915b7951 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -10,6 +10,9 @@ however, it has to be formatted properly to pass verification tests. ## [Unreleased] +### Added +- Initial version of Project Wide Actions for pre-release (`InputSystem.actions`). This feature is available only on Unity Editor versions 2022.3 and above and can be modified in the Project Settings. + ## [1.7.0] - 2023-08-14 ### Added diff --git a/Packages/com.unity.inputsystem/Documentation~/Images/ProjectSettingsInputActions.png b/Packages/com.unity.inputsystem/Documentation~/Images/ProjectSettingsInputActions.png new file mode 100644 index 0000000000000000000000000000000000000000..e19e192ca0283d1829a3b57673da137bed06693e GIT binary patch literal 98794 zcmdqIX*|^J`#0XAP?4oDjBJUrHO4abvL+%?c0xkdv6H0*kulnA83|>H$Ub&4W-tu0 zmMzAAJeE@BRD!9^C(@|0iv)S0CqcoX2szmm}h)jt1Qc_7jH=9ir2`rh4np zp`+wOhmM?~IS&2>d2g@b(4j|%G*zz}_*%>*`E}hM_p9C_Ucyhb{jeDCyi#)o|5@%<#}o(Bfp^5bR)Y_`rN`@nGL|E%;y~ zxcgwY?ZDt*&GMl8C$-Y%)lLc9tHiC4gU#pz!7p=42P;b1i@^swQML#Biw8qSc!|q< zt_SO`QM$2ucUBIz^A1clq z>0R75ubK7P%T;on{QFPCYZd!@*<6B0FL6ave-QC7b|B^`Nk%J1$x1yibTDgv@7htb z1b>3=aW9g%99(-RVxIesvQ@+{ql+uXj|j{?l`q&>-*kkcSm8lD<=7<+ zhLOl4>1*SAol1N9L{^%7)cTFG&5s1GXFW*y`St(!=|O)MHQ!vZKh)J$ZItVr&{K{z zS3M$6Dohq6T`j*o@x?Qj%CD=2=~qYX9SAKl!WA-TlD;KK+Je?U$Le47j&P%ODTYp6 zS-Z$6+Ce9)X@1`PvUy{rj^%^FxLVenlY7zNFptG9Wr^u^ZfP}*ui12*rjFl#7QFck zA7qfcp5-NM$8JQ(SWlVWO*8QOv!q;bI{&Os>64JlGnTIXA{|1;o_$#f`StwyU7M$e zz3h$8kiUJ-i4%05ACQF6g&n^b z@RZ2muP(Ommwk@Bp|JryPa^zErt)SX8~YYAmyI$F}|N0RQ+i4 zsmGohayRx?+Hll2zW_p;mU zZ)z!N@Z|1~YOjbrcZ(%L?1E5Gw=6&JoHiq#PNrd|pASUZX|Fw+WT6#(5$iFsIW~G| zmdbYRSOeGN^o{xc-c9i}mFG(7n|vLoLv>3cciN+MY$aRWCyFilKUG&f+>mn4;0mQY zga5o<%3t;$_vZOI6Eo1%^8%0aY$C2{JdwEA*>N$BA&zZQe_Kj+l4(XSTuY7!YrEhr zQQ9qy@yb6fV|%8+0*5`3uRpg~{ z$o_afC=02uvZUV+KPU~}DIF)It<_d4e%HWEymq!W?mpR)owE+v$e1s({LdiVHyir< zP=h`8K1F-!6*+Q2U&rc+20fABBW}JKI`cNV{LK6(hY0To_i2lS)^B+6kA+quhebt1 z4CRBbzkugW!`$n}!?e1y3j(e%jXY3$LH_*u=Cw!Z{8+MD$FXsJR?by5Tx4>MYGB9G z$vl<*{np{#8*77$L8VK!Cu3KdWMjHzn+q²N=Sb(RJ-TwRdf_e|qwhMo@5tx*W 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 z z6ez3m2BL?#jofT$bLTJsRBrx?qD9g)`%REsK#51w!Xs-z*aYQPqc%bbN+fd@8qPDK ze3@{Rh}`p>Y+_i)TW9fy@Y_}R7Ek>{Qbqik@5TMThZWC(tTZ12YcL*H5{Dgf4pOxn zgz-M|4hhI@`SbQ(x11uk9UyRB_q1WzDe}sz6&iHny0S@tS8KzvNB-AelcQMZ{36EjsfoREj$9fzmDE~Y^7O}30(ibgAJ}Ca-qaFi z-)u_je<5syq#BJ&3|QBu7JTaNpVzzAS@-r~myDHOJik$&=UPUj+ti6q30D&rzUlhL^M%Z_1vSq`K0nlD zQ;g&!#HE=vS$>n`uPu9Y9>1K8B2!^Qyy-TBzu9n7Wu75+T1hSx z3)_mwHp~!jGvW3u=lo)!7hoD{~ z9Hnf=mv^;u9y0&!;%k!FXXhvVD&1W^hwXo(%xkTItwiWb{SrKU0hK$CvwB+UknW=O z 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 +*The Input Actions settings panel in the Project Settings window, showing the default player actions.* + +The project-wide actions feature has some default action maps set up, which you can add to, modify or delete. They are actions which are useful in typical games, such as moving a player character with WSAD keys or a Joypad stick, pressing a button to jump or interact, as well as common UI controls such as pointing, submitting, or canceling within a user interface. + +**Note**: If you’d like to delete all the default actions so that you can start from an empty configuration, you don’t need to delete the actions individually. You can delete the default Action Maps, which deletes all the Actions contained in those maps in one go. + +### Reading project-wide actions + +You can access the project-wide actions in your script by using the [InputSystem.actions](../api/UnityEngine.InputSystem.InputSystem.html#UnityEngine_InputSystem_InputSystem_actions) property. For example: + + var myAction = InputSystem.actions.FindAction("Player/Jump"); + +The above line of code reads the "Jump" action, from the “Player” action map, which is one of the default actions that comes with the new project-wide actions feature. + +Unlike Input Action assets, the project-wide actions are stored in your project’s Project Settings folder, so they do not appear in your Project window. The **InputSystem.actions** property is a built-in reference to that asset. This means you can use all the same techniques described throughout the rest of the documentation about [using action assets](Workflow-ActionsAsset.html), but instead of referencing an asset from your project, you can use the **InputSystem.actions property** in your scripts to reference the project-wide actions. + +For example, here is the script from the [Action Assets workflow page](Workflow-ActionsAsset.html), adapted to use the project-wide actions, and the default actions in the "Player" action map. + +``` +using UnityEngine; +using UnityEngine.InputSystem; + +public class ExampleScript : MonoBehaviour +{ + // private field to store move action reference + private InputAction moveAction; + + void Awake() + { + // find the "move" action, and keep the reference to it, for use in Update + moveAction = InputSystem.actions.FindAction("Player/move"); + // for the "jump" action, we add a callback method for when it is performed + InputSystem.actions.FindAction("Player/jump").performed += OnJump; + } + + void Update() + { + // our update loop polls the "move" action value each frame + Vector2 moveVector = moveAction.ReadValue(); + } + + private void OnJump(InputAction.CallbackContext context) + { + // this is the "jump" action callback method + Debug.Log("Jump!"); + } +} +``` + +Things to note about the above example script, as compared to the script on the [Action Assets workflow page](Workflow-ActionsAsset.html): + +* Because there is a built-in reference to the project-wide actions, you do not need a public field with an assigned asset to get a reference to the actions. + +* This script does not enable or disable action maps. Project-wide action maps are enabled by default. This means unlike with Action assets, you do not need to enable individual action maps in your script before being able to use them. You may still want to disable or enable action maps if you want to make use of different types of input in different parts of your project. + +### Limitations + +Because this is a pre-release, the project-wide actions feature is not yet complete and has some limitations you should be aware of. + +**The project-wide actions cannot be referenced in an ActionsAsset field in the inspector.** + +You can't assign the project-wide input actions asset to UI fields where you would normally assign an input action asset. For example, if you are using the PlayerInput component, you can’t assign the project wide actions to its "Actions" field. This means if you want to use the PlayerInput component, you must create an Actions asset and set up your input configuration there instead of in the project-wide actions. + +**Some features of the project-wide actions editor are different, or missing, compared with the Actions Editor window for actions assets.** + +Although the UI to edit the project-wide actions in the Project Settings window is very similar to the Actions Editor for action assets, there are some differences and missing features. In particular, the new project-wide actions editor uses a newer UI system, and therefore there are some cosmetic differences such as different icons, and some workflow features are missing such as some of the keyboard shortcuts. You also cannot yet access the project-wide actions [through a C# wrapper](Workflow-ActionsAsset.html#referencing-the-actions-asset-through-a-c-wrapper). diff --git a/Packages/com.unity.inputsystem/Documentation~/TableOfContents.md b/Packages/com.unity.inputsystem/Documentation~/TableOfContents.md index 5db6f652d9..16d0cf0671 100644 --- a/Packages/com.unity.inputsystem/Documentation~/TableOfContents.md +++ b/Packages/com.unity.inputsystem/Documentation~/TableOfContents.md @@ -1,4 +1,5 @@ +* [Important Pre-Release Notes](PreReleaseNotes.md) * [Introduction](index.md) * [Installation](Installation.md) * [Concepts](Concepts.md) diff --git a/Packages/com.unity.inputsystem/Documentation~/config.json b/Packages/com.unity.inputsystem/Documentation~/config.json new file mode 100644 index 0000000000..43dc9e6119 --- /dev/null +++ b/Packages/com.unity.inputsystem/Documentation~/config.json @@ -0,0 +1 @@ +{ "DefineConstants": "UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS" } \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector2Composite.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector2Composite.cs index 1aa572b8ef..20fb8e69f5 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector2Composite.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector2Composite.cs @@ -204,7 +204,7 @@ public override void OnGUI() target.mode = (Vector2Composite.Mode)EditorGUILayout.EnumPopup(m_ModeLabel, target.mode); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) { var modeField = new EnumField("Mode", target.mode) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector3Composite.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector3Composite.cs index 77b7cc5950..8af9bd4a6d 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector3Composite.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Composites/Vector3Composite.cs @@ -177,7 +177,7 @@ public override void OnGUI() target.mode = (Vector2Composite.Mode)EditorGUILayout.EnumPopup(m_ModeLabel, target.mode); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) { var modeField = new EnumField("Mode", target.mode) diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputControlScheme.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputControlScheme.cs index 070a25721e..bb15893a61 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputControlScheme.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputControlScheme.cs @@ -112,7 +112,7 @@ public InputControlScheme(string name, IEnumerable devices = } } - #if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR + #if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS internal InputControlScheme(SerializedProperty sp) { var requirements = new List(); diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/HoldInteraction.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/HoldInteraction.cs index 4ef657aee7..63c6d6106c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/HoldInteraction.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/HoldInteraction.cs @@ -128,7 +128,7 @@ public override void OnGUI() m_DurationSetting.OnGUI(); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) { m_PressPointSetting.OnDrawVisualElements(root, onChangedCallback); diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/MultiTapInteraction.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/MultiTapInteraction.cs index 513c05de5b..93138bcaec 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/MultiTapInteraction.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/MultiTapInteraction.cs @@ -202,7 +202,7 @@ public override void OnGUI() m_PressPointSetting.OnGUI(); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) { var tapCountField = new IntegerField(m_TapCountLabel.text) { value = target.tapCount }; diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/PressInteraction.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/PressInteraction.cs index b9ad14634b..91e32e33fb 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/PressInteraction.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/PressInteraction.cs @@ -218,7 +218,7 @@ public override void OnGUI() m_PressPointSetting.OnGUI(); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) { root.Add(new HelpBox(s_HelpBoxText.text, HelpBoxMessageType.None)); diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/SlowTapInteraction.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/SlowTapInteraction.cs index a800fedeaf..fd395883a3 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/SlowTapInteraction.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/SlowTapInteraction.cs @@ -92,7 +92,7 @@ public override void OnGUI() m_PressPointSetting.OnGUI(); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) { m_DurationSetting.OnDrawVisualElements(root, onChangedCallback); diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/TapInteraction.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/TapInteraction.cs index 6bc6b6fb12..b7199820de 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/TapInteraction.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Actions/Interactions/TapInteraction.cs @@ -106,7 +106,7 @@ public override void OnGUI() m_PressPointSetting.OnGUI(); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) { m_DurationSetting.OnDrawVisualElements(root, onChangedCallback); diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/Processors/AxisDeadzoneProcessor.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/Processors/AxisDeadzoneProcessor.cs index 4713b2e67a..cb8e93d1af 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/Processors/AxisDeadzoneProcessor.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/Processors/AxisDeadzoneProcessor.cs @@ -97,7 +97,7 @@ public override void OnGUI() m_MaxSetting.OnGUI(); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) { m_MinSetting.OnDrawVisualElements(root, onChangedCallback); diff --git a/Packages/com.unity.inputsystem/InputSystem/Controls/Processors/StickDeadzoneProcessor.cs b/Packages/com.unity.inputsystem/InputSystem/Controls/Processors/StickDeadzoneProcessor.cs index bcb4cf4d0b..14e8511231 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Controls/Processors/StickDeadzoneProcessor.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Controls/Processors/StickDeadzoneProcessor.cs @@ -86,7 +86,7 @@ public override void OnGUI() m_MaxSetting.OnGUI(); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public override void OnDrawVisualElements(VisualElement root, Action onChangedCallback) { m_MinSetting.OnDrawVisualElements(root, onChangedCallback); diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionEditorWindow.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionEditorWindow.cs index 09ac63ad88..6a942a521b 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionEditorWindow.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/InputActionEditorWindow.cs @@ -37,8 +37,8 @@ internal class InputActionEditorWindow : EditorWindow, IDisposable [OnOpenAsset] public static bool OnOpenAsset(int instanceId, int line) { -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR - if (InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseUIToolkitEditor) && InputActionsEditorWindow.isWindowEnabled) +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + if (InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseUIToolkitEditorForAllAssets)) return false; #endif var path = AssetDatabase.GetAssetPath(instanceId); diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/ParameterListView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/ParameterListView.cs index 4705cb16cf..c51a564066 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/ParameterListView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetEditor/ParameterListView.cs @@ -247,7 +247,7 @@ public void Clear() m_ParameterEditor = null; } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS public void OnDrawVisualElements(VisualElement root) { if (m_ParameterEditor != null) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/InputParameterEditor.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/InputParameterEditor.cs index dbe05472ef..dfaba6ccb8 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/InputParameterEditor.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/InputParameterEditor.cs @@ -32,7 +32,7 @@ public abstract class InputParameterEditor /// public abstract void OnGUI(); -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS /// /// Add visual elements for this parameter editor to a root VisualElement. /// @@ -182,7 +182,7 @@ internal override void SetTarget(object target) OnEnable(); } -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS /// /// Default stub implementation of . /// Should be overridden to create the desired UI. @@ -223,7 +223,7 @@ public void Initialize(string label, string tooltip, string defaultName, Func o != null && o.name == kAssetName) as InputActionAsset; + if (inputActionsAsset != null) + return inputActionsAsset; + } + + return CreateNewActionAsset(); + } + + private static InputActionAsset CreateNewActionAsset() + { + var json = File.ReadAllText(Path.Combine(Environment.CurrentDirectory, s_DefaultAssetPath)); + + var asset = ScriptableObject.CreateInstance(); + asset.LoadFromJson(json); + asset.name = kAssetName; + + AssetDatabase.AddObjectToAsset(asset, s_AssetPath); + + // Make sure all the elements in the asset have GUIDs and that they are indeed unique. + var maps = asset.actionMaps; + foreach (var map in maps) + { + // Make sure action map has GUID. + if (string.IsNullOrEmpty(map.m_Id) || asset.actionMaps.Count(x => x.m_Id == map.m_Id) > 1) + map.GenerateId(); + + // Make sure all actions have GUIDs. + foreach (var action in map.actions) + { + var actionId = action.m_Id; + if (string.IsNullOrEmpty(actionId) || asset.actionMaps.Sum(m => m.actions.Count(a => a.m_Id == actionId)) > 1) + action.GenerateId(); + } + + // Make sure all bindings have GUIDs. + for (var i = 0; i < map.m_Bindings.LengthSafe(); ++i) + { + var bindingId = map.m_Bindings[i].m_Id; + if (string.IsNullOrEmpty(bindingId) || asset.actionMaps.Sum(m => m.bindings.Count(b => b.m_Id == bindingId)) > 1) + map.m_Bindings[i].GenerateId(); + } + } + + // Create sub-asset for each action. This is so that users can select individual input actions from the asset when they're + // trying to assign to a field that accepts only one action. + foreach (var map in maps) + { + foreach (var action in map.actions) + { + var actionReference = ScriptableObject.CreateInstance(); + actionReference.Set(action); + AssetDatabase.AddObjectToAsset(actionReference, asset); + } + } + + AssetDatabase.SaveAssets(); + + return asset; + } + } +} +#endif diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs.meta new file mode 100644 index 0000000000..02e07768b0 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2a47e68520f79264fb6828f8f9516ef2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions new file mode 100644 index 0000000000..31333c2493 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions @@ -0,0 +1,1017 @@ +{ + "name": "ProjectWideActionsTemplate", + "maps": [ + { + "name": "Player", + "id": "df70fa95-8a34-4494-b137-73ab6b9c7d37", + "actions": [ + { + "name": "Move", + "type": "Value", + "id": "351f2ccd-1f9f-44bf-9bec-d62ac5c5f408", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Look", + "type": "Value", + "id": "6b444451-8a00-4d00-a97e-f47457f736a8", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Attack", + "type": "Button", + "id": "6c2ab1b8-8984-453a-af3d-a3c78ae1679a", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Interact", + "type": "Button", + "id": "852140f2-7766-474d-8707-702459ba45f3", + "expectedControlType": "Button", + "processors": "", + "interactions": "Hold", + "initialStateCheck": false + }, + { + "name": "Crouch", + "type": "Button", + "id": "27c5f898-bc57-4ee1-8800-db469aca5fe3", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Jump", + "type": "Button", + "id": "f1ba0d36-48eb-4cd5-b651-1c94a6531f70", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Previous", + "type": "Value", + "id": "2776c80d-3c14-4091-8c56-d04ced07a2b0", + "expectedControlType": "Axis", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Next", + "type": "Value", + "id": "b7230bb6-fc9b-4f52-8b25-f5e19cb2c2ba", + "expectedControlType": "Axis", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Sprint", + "type": "Button", + "id": "641cd816-40e6-41b4-8c3d-04687c349290", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + } + ], + "bindings": [ + { + "name": "", + "id": "978bfe49-cc26-4a3d-ab7b-7d7a29327403", + "path": "/leftStick", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "WASD", + "id": "00ca640b-d935-4593-8157-c05846ea39b3", + "path": "Dpad", + "interactions": "", + "processors": "", + "groups": "", + "action": "Move", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "e2062cb9-1b15-46a2-838c-2f8d72a0bdd9", + "path": "/w", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "8180e8bd-4097-4f4e-ab88-4523101a6ce9", + "path": "/upArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "320bffee-a40b-4347-ac70-c210eb8bc73a", + "path": "/s", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "1c5327b5-f71c-4f60-99c7-4e737386f1d1", + "path": "/downArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "d2581a9b-1d11-4566-b27d-b92aff5fabbc", + "path": "/a", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "2e46982e-44cc-431b-9f0b-c11910bf467a", + "path": "/leftArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "fcfe95b8-67b9-4526-84b5-5d0bc98d6400", + "path": "/d", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "77bff152-3580-4b21-b6de-dcd0c7e41164", + "path": "/rightArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "1635d3fe-58b6-4ba9-a4e2-f4b964f6b5c8", + "path": "/{Primary2DAxis}", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "3ea4d645-4504-4529-b061-ab81934c3752", + "path": "/stick", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "c1f7a91b-d0fd-4a62-997e-7fb9b69bf235", + "path": "/rightStick", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8c8e490b-c610-4785-884f-f04217b23ca4", + "path": "/delta", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse;Touch", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "3e5f5442-8668-4b27-a940-df99bad7e831", + "path": "/{Hatswitch}", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "143bb1cd-cc10-4eca-a2f0-a3664166fe91", + "path": "/buttonWest", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "05f6913d-c316-48b2-a6bb-e225f14c7960", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "886e731e-7071-4ae4-95c0-e61739dad6fd", + "path": "/primaryTouch/tap", + "interactions": "", + "processors": "", + "groups": ";Touch", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "ee3d0cd2-254e-47a7-a8cb-bc94d9658c54", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8255d333-5683-4943-a58a-ccb207ff1dce", + "path": "/{PrimaryAction}", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b3c1c7f0-bd20-4ee7-a0f1-899b24bca6d7", + "path": "/enter", + "interactions": "", + "processors": "", + "groups": "", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "cbac6039-9c09-46a1-b5f2-4e5124ccb5ed", + "path": "/2", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Next", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "e15ca19d-e649-4852-97d5-7fe8ccc44e94", + "path": "/dpad/right", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Next", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "f2e9ba44-c423-42a7-ad56-f20975884794", + "path": "/leftShift", + "interactions": "", + "processors": "", + "groups": "", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8cbb2f4b-a784-49cc-8d5e-c010b8c7f4e6", + "path": "/leftStickPress", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "d8bf24bf-3f2f-4160-a97c-38ec1eb520ba", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "eb40bb66-4559-4dfa-9a2f-820438abb426", + "path": "/space", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "daba33a1-ad0c-4742-a909-43ad1cdfbeb6", + "path": "/buttonSouth", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "603f3daf-40bd-4854-8724-93e8017f59e3", + "path": "/secondaryButton", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "1534dc16-a6aa-499d-9c3a-22b47347b52a", + "path": "/1", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Previous", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "25060bbd-a3a6-476e-8fba-45ae484aad05", + "path": "/dpad/left", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Previous", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "1c04ea5f-b012-41d1-a6f7-02e963b52893", + "path": "/e", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Interact", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b3f66d0b-7751-423f-908b-a11c5bd95930", + "path": "/buttonNorth", + "interactions": "", + "processors": "", + "groups": "", + "action": "Interact", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4f4649ac-64a8-4a73-af11-b3faef356a4d", + "path": "/buttonEast", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Crouch", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "36e52cba-0905-478e-a818-f4bfcb9f3b9a", + "path": "/c", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Crouch", + "isComposite": false, + "isPartOfComposite": false + } + ] + }, + { + "name": "UI", + "id": "272f6d14-89ba-496f-b7ff-215263d3219f", + "actions": [ + { + "name": "Navigate", + "type": "PassThrough", + "id": "c95b2375-e6d9-4b88-9c4c-c5e76515df4b", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Submit", + "type": "Button", + "id": "7607c7b6-cd76-4816-beef-bd0341cfe950", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Cancel", + "type": "Button", + "id": "15cef263-9014-4fd5-94d9-4e4a6234a6ef", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Point", + "type": "PassThrough", + "id": "32b35790-4ed0-4e9a-aa41-69ac6d629449", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Click", + "type": "PassThrough", + "id": "3c7022bf-7922-4f7c-a998-c437916075ad", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "RightClick", + "type": "PassThrough", + "id": "44b200b1-1557-4083-816c-b22cbdf77ddf", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "MiddleClick", + "type": "PassThrough", + "id": "dad70c86-b58c-4b17-88ad-f5e53adf419e", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "ScrollWheel", + "type": "PassThrough", + "id": "0489e84a-4833-4c40-bfae-cea84b696689", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": false + } + ], + "bindings": [ + { + "name": "Gamepad", + "id": "809f371f-c5e2-4e7a-83a1-d867598f40dd", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "14a5d6e8-4aaf-4119-a9ef-34b8c2c548bf", + "path": "/leftStick/up", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "9144cbe6-05e1-4687-a6d7-24f99d23dd81", + "path": "/rightStick/up", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "2db08d65-c5fb-421b-983f-c71163608d67", + "path": "/leftStick/down", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "58748904-2ea9-4a80-8579-b500e6a76df8", + "path": "/rightStick/down", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "8ba04515-75aa-45de-966d-393d9bbd1c14", + "path": "/leftStick/left", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "712e721c-bdfb-4b23-a86c-a0d9fcfea921", + "path": "/rightStick/left", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "fcd248ae-a788-4676-a12e-f4d81205600b", + "path": "/leftStick/right", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "1f04d9bc-c50b-41a1-bfcc-afb75475ec20", + "path": "/rightStick/right", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "fb8277d4-c5cd-4663-9dc7-ee3f0b506d90", + "path": "/dpad", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "Joystick", + "id": "e25d9774-381c-4a61-b47c-7b6b299ad9f9", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "3db53b26-6601-41be-9887-63ac74e79d19", + "path": "/stick/up", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "0cb3e13e-3d90-4178-8ae6-d9c5501d653f", + "path": "/stick/down", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "0392d399-f6dd-4c82-8062-c1e9c0d34835", + "path": "/stick/left", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "942a66d9-d42f-43d6-8d70-ecb4ba5363bc", + "path": "/stick/right", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Keyboard", + "id": "ff527021-f211-4c02-933e-5976594c46ed", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "563fbfdd-0f09-408d-aa75-8642c4f08ef0", + "path": "/w", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "eb480147-c587-4a33-85ed-eb0ab9942c43", + "path": "/upArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "2bf42165-60bc-42ca-8072-8c13ab40239b", + "path": "/s", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "85d264ad-e0a0-4565-b7ff-1a37edde51ac", + "path": "/downArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "74214943-c580-44e4-98eb-ad7eebe17902", + "path": "/a", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "cea9b045-a000-445b-95b8-0c171af70a3b", + "path": "/leftArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "8607c725-d935-4808-84b1-8354e29bab63", + "path": "/d", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "4cda81dc-9edd-4e03-9d7c-a71a14345d0b", + "path": "/rightArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "9e92bb26-7e3b-4ec4-b06b-3c8f8e498ddc", + "path": "*/{Submit}", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse;Gamepad;Touch;Joystick;XR", + "action": "Submit", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "82627dcc-3b13-4ba9-841d-e4b746d6553e", + "path": "*/{Cancel}", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse;Gamepad;Touch;Joystick;XR", + "action": "Cancel", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "c52c8e0b-8179-41d3-b8a1-d149033bbe86", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "e1394cbc-336e-44ce-9ea8-6007ed6193f7", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "5693e57a-238a-46ed-b5ae-e64e6e574302", + "path": "/touch*/position", + "interactions": "", + "processors": "", + "groups": "Touch", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4faf7dc9-b979-4210-aa8c-e808e1ef89f5", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8d66d5ba-88d7-48e6-b1cd-198bbfef7ace", + "path": "/tip", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "47c2a644-3ebc-4dae-a106-589b7ca75b59", + "path": "/touch*/press", + "interactions": "", + "processors": "", + "groups": "Touch", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "bb9e6b34-44bf-4381-ac63-5aa15d19f677", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "38c99815-14ea-4617-8627-164d27641299", + "path": "/scroll", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "ScrollWheel", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4c191405-5738-4d4b-a523-c6a301dbf754", + "path": "/rightButton", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "RightClick", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "24066f69-da47-44f3-a07e-0015fb02eb2e", + "path": "/middleButton", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "MiddleClick", + "isComposite": false, + "isPartOfComposite": false + } + ] + } + ], + "controlSchemes": [ + { + "name": "Keyboard&Mouse", + "bindingGroup": "Keyboard&Mouse", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + }, + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Gamepad", + "bindingGroup": "Gamepad", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Touch", + "bindingGroup": "Touch", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Joystick", + "bindingGroup": "Joystick", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "XR", + "bindingGroup": "XR", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + } + ] +} \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions.meta b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions.meta new file mode 100644 index 0000000000..c27a6f7bab --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 8a190d472354bb845973412ac9c5c477 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3} + generateWrapperCode: 0 + wrapperCodePath: + wrapperClassName: + wrapperCodeNamespace: diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputSettingsBuildProvider.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputSettingsBuildProvider.cs index d22c0b2f30..17d3fbd9ca 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputSettingsBuildProvider.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputSettingsBuildProvider.cs @@ -9,21 +9,31 @@ namespace UnityEngine.InputSystem.Editor { internal class InputSettingsBuildProvider : IPreprocessBuildWithReport, IPostprocessBuildWithReport { + InputActionAsset m_ProjectWideActions; + Object[] m_OriginalPreloadedAssets; public int callbackOrder => 0; public void OnPreprocessBuild(BuildReport report) { - if (InputSystem.settings == null) - return; + m_OriginalPreloadedAssets = PlayerSettings.GetPreloadedAssets(); + var preloadedAssets = PlayerSettings.GetPreloadedAssets(); - // If we operate on temporary object instead of input setting asset, - // adding temporary asset would result in preloadedAssets containing null object "{fileID: 0}". - // Hence we ignore adding temporary objects to preloaded assets. - if (!EditorUtility.IsPersistent(InputSystem.settings)) +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + EditorBuildSettings.TryGetConfigObject(InputSettingsProvider.kEditorBuildSettingsActionsConfigKey, + out m_ProjectWideActions); + + if (m_ProjectWideActions != null) + { + if (!preloadedAssets.Contains(InputSystem.actions)) + { + ArrayHelpers.Append(ref preloadedAssets, InputSystem.actions); + PlayerSettings.SetPreloadedAssets(preloadedAssets); + } + } +#endif + if (InputSystem.settings == null) return; - // Add InputSettings object assets, if it's not in there already. - var preloadedAssets = PlayerSettings.GetPreloadedAssets(); if (!preloadedAssets.Contains(InputSystem.settings)) { ArrayHelpers.Append(ref preloadedAssets, InputSystem.settings); @@ -33,19 +43,8 @@ public void OnPreprocessBuild(BuildReport report) public void OnPostprocessBuild(BuildReport report) { - // Revert back to original state by removing all input settings from preloaded assets. - var preloadedAssets = PlayerSettings.GetPreloadedAssets(); - while (preloadedAssets != null && preloadedAssets.Length > 0) - { - var index = preloadedAssets.IndexOf(x => x is InputSettings); - if (index != -1) - { - ArrayHelpers.EraseAt(ref preloadedAssets, index); - PlayerSettings.SetPreloadedAssets(preloadedAssets); - } - else - break; - } + // Revert back to original state + PlayerSettings.SetPreloadedAssets(m_OriginalPreloadedAssets); } } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputSettingsProvider.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputSettingsProvider.cs index 83379fc536..7bb317014c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputSettingsProvider.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Settings/InputSettingsProvider.cs @@ -16,6 +16,7 @@ namespace UnityEngine.InputSystem.Editor internal class InputSettingsProvider : SettingsProvider, IDisposable { public const string kEditorBuildSettingsConfigKey = "com.unity.input.settings"; + public const string kEditorBuildSettingsActionsConfigKey = "com.unity.input.settings.actions"; public const string kSettingsPath = "Project/Input System Package"; public static void Open() diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/Commands.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/Commands.cs index eabfc72754..e875462882 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/Commands.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/Commands.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Linq; @@ -303,7 +303,7 @@ public static Command SaveAsset() { return (in InputActionsEditorState state) => { - InputActionsEditorWindow.SaveAsset(state.serializedObject); + InputActionsEditorWindowUtils.SaveAsset(state.serializedObject); return state; }; } @@ -316,7 +316,7 @@ public static Command ToggleAutoSave(bool newValue) { // If it changed from disabled to enabled, perform an initial save. if (newValue) - InputActionsEditorWindow.SaveAsset(state.serializedObject); + InputActionsEditorWindowUtils.SaveAsset(state.serializedObject); InputEditorUserSettings.autoSaveInputActionAssets = newValue; } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs index f0682542eb..d60cf1b624 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Linq; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorSettingsProvider.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorSettingsProvider.cs index 801506b3c2..73b7065fec 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorSettingsProvider.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorSettingsProvider.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR // We use some UITK controls that are only available in 2022.2 or later (MultiColumnListView for example) +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System.Collections.Generic; using UnityEditor; using UnityEngine.UIElements; @@ -9,6 +9,10 @@ internal class InputActionsEditorSettingsProvider : SettingsProvider { public const string kSettingsPath = "Project/Input System Package/Actions"; + [SerializeField] InputActionsEditorState m_State; + VisualElement m_RootVisualElement; + StateContainer m_StateContainer; + public InputActionsEditorSettingsProvider(string path, SettingsScope scopes, IEnumerable keywords = null) : base(path, scopes, keywords) { @@ -16,16 +20,31 @@ public InputActionsEditorSettingsProvider(string path, SettingsScope scopes, IEn public override void OnActivate(string searchContext, VisualElement rootElement) { - var visualElement = Resources.Load("InputActionsEditor"); - visualElement.CloneTree(rootElement); + m_RootVisualElement = rootElement; + var asset = ProjectWideActionsAsset.GetOrCreate(); + var serializedAsset = new SerializedObject(asset); + m_State = new InputActionsEditorState(serializedAsset); + BuildUI(); + } + + private void OnStateChanged(InputActionsEditorState newState) + { + if (InputEditorUserSettings.autoSaveInputActionAssets) + InputActionsEditorWindowUtils.SaveAsset(m_State.serializedObject); + } + + private void BuildUI() + { + m_StateContainer = new StateContainer(m_RootVisualElement, m_State); + m_StateContainer.StateChanged += OnStateChanged; + m_RootVisualElement.styleSheets.Add(InputActionsEditorWindowUtils.theme); + new InputActionsEditorView(m_RootVisualElement, m_StateContainer); + m_StateContainer.Initialize(); } [SettingsProvider] public static SettingsProvider CreateGlobalInputActionsEditorProvider() { - if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseUIToolkitEditor)) - return null; - var provider = new InputActionsEditorSettingsProvider(kSettingsPath, SettingsScope.Project) { label = "Input Actions" diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorState.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorState.cs index 5a2ab79378..ca9f754b55 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorState.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorState.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Collections.ObjectModel; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs index 8e7351c3a9..57b58db415 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs @@ -1,6 +1,6 @@ // UITK TreeView is not supported in earlier versions // Therefore the UITK version of the InputActionAsset Editor is not available on earlier Editor versions either. -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.IO; using System.Linq; @@ -16,21 +16,18 @@ internal static class EnableUITKEditor { static EnableUITKEditor() { - // set this feature flag to true to enable the UITK editor - InputSystem.settings.SetInternalFeatureFlag(InputFeatureNames.kUseUIToolkitEditor, false); + // Controls whether the UITK version of the InputActionAsset Editor is enabled or not for + // editing standalone user Input Action assets. + // At the moment, the UITK Asset Editor doesn't have feature parity with the IMGUI version. + // This is set to false to show the IMGUI version of the InputActionAsset Editor instead. + // UITK Editor is always be used for the Project Settings Editor regardless of this setting. + InputSystem.settings.SetInternalFeatureFlag(InputFeatureNames.kUseUIToolkitEditorForAllAssets, false); } } internal class InputActionsEditorWindow : EditorWindow { private static readonly string k_FileExtension = "." + InputActionAsset.Extension; - /// - /// Controls whether the UITK version of the InputActionAsset Editor is enabled or not for editing Input Action - /// assets. - /// - /// At the moment, the UITK Asset Editor doesn't have feature parity with the IMGUI version. - /// This is set to false to show the IMGUI version of the InputActionAsset Editor instead. - internal static bool isWindowEnabled = false; private int m_AssetId; private string m_AssetPath; private string m_AssetJson; @@ -39,7 +36,7 @@ internal class InputActionsEditorWindow : EditorWindow [OnOpenAsset] public static bool OpenAsset(int instanceId, int line) { - if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseUIToolkitEditor) || !isWindowEnabled) + if (!InputSystem.settings.IsFeatureEnabled(InputFeatureNames.kUseUIToolkitEditorForAllAssets)) return false; var path = AssetDatabase.GetAssetPath(instanceId); @@ -136,7 +133,7 @@ private void OnStateChanged(InputActionsEditorState newState) { DirtyInputActionsEditorWindow(newState); if (InputEditorUserSettings.autoSaveInputActionAssets) - SaveAsset(m_State.serializedObject); + InputActionsEditorWindowUtils.SaveAsset(m_State.serializedObject); } private void DirtyInputActionsEditorWindow(InputActionsEditorState newState) @@ -170,7 +167,7 @@ private void ConfirmSaveChangesIfNeeded() switch (result) { case 0: // Save - SaveAsset(m_State.serializedObject, this); + InputActionsEditorWindowUtils.SaveAsset(m_State.serializedObject); break; case 1: // Cancel editor quit. (open new editor window with the edited asset) ReshowEditorWindowWithUnsavedChanges(); @@ -207,24 +204,6 @@ private InputActionAsset GetAssetFromDatabase() [SerializeField] private InputActionsEditorState m_State; [SerializeField] private string m_AssetGUID; - - public static void SaveAsset(SerializedObject serializedAsset, InputActionsEditorWindow currentWindow = null) - { - if ((focusedWindow == null || focusedWindow is not InputActionsEditorWindow) && currentWindow == null) - return; - currentWindow = currentWindow ? currentWindow : (InputActionsEditorWindow)focusedWindow; - var asset = (InputActionAsset)serializedAsset.targetObject; - var assetJson = asset.ToJson(); - - var existingJson = File.ReadAllText(currentWindow.m_AssetPath); - if (assetJson != existingJson) - { - EditorHelpers.CheckOut(currentWindow.m_AssetPath); - File.WriteAllText(currentWindow.m_AssetPath, assetJson); - AssetDatabase.ImportAsset(currentWindow.m_AssetPath); - currentWindow.m_AssetJson = assetJson; - } - } } } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindowUtils.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindowUtils.cs new file mode 100644 index 0000000000..1ace6c81bd --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindowUtils.cs @@ -0,0 +1,35 @@ +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS +using System.IO; +using UnityEditor; +using UnityEngine.UIElements; + +namespace UnityEngine.InputSystem.Editor +{ + internal class InputActionsEditorWindowUtils + { + public static StyleSheet theme => EditorGUIUtility.isProSkin + ? AssetDatabase.LoadAssetAtPath(InputActionsEditorConstants.PackagePath + InputActionsEditorConstants.ResourcesPath + "/InputAssetEditorDark.uss") + : AssetDatabase.LoadAssetAtPath(InputActionsEditorConstants.PackagePath + InputActionsEditorConstants.ResourcesPath + "/InputAssetEditorLight.uss"); + + public static void SaveAsset(SerializedObject serializedAsset) + { + var asset = (InputActionAsset)serializedAsset.targetObject; + // for the global actions asset: save differently (as it is a yaml file and not a json) + if (asset.name == ProjectWideActionsAsset.kAssetName) + { + AssetDatabase.SaveAssets(); + return; + } + var assetPath = AssetDatabase.GetAssetPath(asset); + var assetJson = asset.ToJson(); + var existingJson = File.Exists(assetPath) ? File.ReadAllText(assetPath) : ""; + if (assetJson != existingJson) + { + EditorHelpers.CheckOut(assetPath); + File.WriteAllText(assetPath, assetJson); + AssetDatabase.ImportAsset(assetPath); + } + } + } +} +#endif diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindowUtils.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindowUtils.cs.meta new file mode 100644 index 0000000000..f1509991e7 --- /dev/null +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindowUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b8e35f20292579409c899395e30948f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditor.uxml b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditor.uxml index f709cfda11..593243960e 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditor.uxml +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditor.uxml @@ -4,16 +4,13 @@ - - - - + diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditorStyles.uss b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditorStyles.uss index 12dce47ad4..e9a6e3f089 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditorStyles.uss +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Resources/InputActionsEditorStyles.uss @@ -164,9 +164,18 @@ } .search-field { + /* hide while not implemented */ + display: none; width: 190px; } +#save-asset-toolbar-container +{ + /* hide while always autosaving in project wide settings */ + display: none; +} + + .unity-two-pane-split-view__dragline-anchor { background-color: rgb(25, 25, 25); } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputAction.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputAction.cs index 223b67c98c..e6792023ba 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputAction.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputAction.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using UnityEditor; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputBinding.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputBinding.cs index 466e924c5c..21bef1c96a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputBinding.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/SerializedInputBinding.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Linq; using UnityEditor; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/StateContainer.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/StateContainer.cs index b9001575b6..04ed660258 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/StateContainer.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/StateContainer.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Linq.Expressions; using UnityEditor; @@ -19,6 +19,7 @@ public StateContainer(VisualElement rootVisualElement, InputActionsEditorState i m_RootVisualElement = rootVisualElement; m_State = initialState; + rootVisualElement.Unbind(); m_RootVisualElement.TrackSerializedObjectValue(initialState.serializedObject, so => { StateChanged?.Invoke(m_State); diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionMapsView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionMapsView.cs index 33f0408a06..184df5dfe8 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionMapsView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionMapsView.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System.Collections.Generic; using System.Linq; using UnityEngine.InputSystem.Utilities; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionPropertiesView.cs index a709074d70..b2fa3edab8 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionPropertiesView.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Linq; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs index 0cfd57ac90..11f1b241be 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ActionsTreeView.cs @@ -1,6 +1,6 @@ // UITK TreeView is not supported in earlier versions // Therefore the UITK version of the InputActionAsset Editor is not available on earlier Editor versions either. -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Linq; @@ -145,21 +145,22 @@ private int GetSelectedElementId(InputActionsEditorState state, List> treeList, int selectedBindingIndex) + private int GetComponentOrBindingID(List> treeItemList, int selectedBindingIndex) { - var currentBindingIndex = -1; - foreach (var action in treeList) + foreach (var actionItem in treeItemList) { - foreach (var bindingOrComponent in action.children) + // Look for the element ID by checking if the selected binding index matches the binding index of + // the ActionOrBindingData of the item. Deals with composite bindings as well. + foreach (var bindingOrComponentItem in actionItem.children) { - currentBindingIndex++; - if (currentBindingIndex == selectedBindingIndex) return bindingOrComponent.id; - if (bindingOrComponent.hasChildren) + if (bindingOrComponentItem.data.bindingIndex == selectedBindingIndex) + return bindingOrComponentItem.id; + if (bindingOrComponentItem.hasChildren) { - foreach (var binding in bindingOrComponent.children) + foreach (var bindingItem in bindingOrComponentItem.children) { - currentBindingIndex++; - if (currentBindingIndex == selectedBindingIndex) return binding.id; + if (bindingOrComponentItem.data.bindingIndex == selectedBindingIndex) + return bindingItem.id; } } } @@ -239,7 +240,7 @@ private void OnKeyDownEvent(KeyDownEvent e) { if (e.keyCode == KeyCode.F2) OnKeyDownEventForRename(); - else if (e.keyCode == KeyCode.Space) + else if (e.keyCode == KeyCode.Delete) OnKeyDownEventForDelete(); } diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs index 1c58ea11a4..9fbaf9d099 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/BindingPropertiesView.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System.Linq; using UnityEditor; using UnityEngine.UIElements; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositeBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositeBindingPropertiesView.cs index 466dc57537..d8af8b7abe 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositeBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositeBindingPropertiesView.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Linq; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositePartBindingPropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositePartBindingPropertiesView.cs index a71a0fa18c..686fc5f936 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositePartBindingPropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/CompositePartBindingPropertiesView.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Linq; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ContextMenu.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ContextMenu.cs index b06ef5d898..fa9a8e4088 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ContextMenu.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ContextMenu.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using UnityEngine.UIElements; namespace UnityEngine.InputSystem.Editor diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ControlSchemesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ControlSchemesView.cs index 02200e33f7..386df4c0d2 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ControlSchemesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ControlSchemesView.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Linq; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionViewsControlsHolder.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionViewsControlsHolder.cs index 39d1ece872..3bdb74eec1 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionViewsControlsHolder.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionViewsControlsHolder.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using UnityEngine.UIElements; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionsEditorView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionsEditorView.cs index ab046b4a20..55bcef0fe5 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionsEditorView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionsEditorView.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Linq; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionsTreeViewItem.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionsTreeViewItem.cs index e26a6bade7..9bd463d4ad 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionsTreeViewItem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/InputActionsTreeViewItem.cs @@ -1,6 +1,6 @@ // UITK TreeView is not supported in earlier versions // Therefore the UITK version of the InputActionAsset Editor is not available on earlier Editor versions either. -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System.Threading.Tasks; using UnityEditor; using UnityEngine.InputSystem.Editor; 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 e28d287739..0c1a7ee04c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/NameAndParametersListView.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Linq; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/PropertiesView.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/PropertiesView.cs index 2c1ed6d523..f1bd92e6fe 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/PropertiesView.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/PropertiesView.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Linq; using UnityEditor; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/Selectors.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/Selectors.cs index 1648b05aac..421bc9fcc1 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/Selectors.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/Selectors.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; using System.Linq; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ViewBase.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ViewBase.cs index 6db29b955d..ed859e2d7c 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ViewBase.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/ViewBase.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using System.Collections.Generic; diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/VisualElementExtensions.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/VisualElementExtensions.cs index fc757cb320..aac90094bb 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/VisualElementExtensions.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Views/VisualElementExtensions.cs @@ -1,4 +1,4 @@ -#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR +#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS using System; using UnityEngine.UIElements; diff --git a/Packages/com.unity.inputsystem/InputSystem/InputFeatureNames.cs b/Packages/com.unity.inputsystem/InputSystem/InputFeatureNames.cs index a6be01b1ac..d28b6c3484 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputFeatureNames.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputFeatureNames.cs @@ -9,8 +9,8 @@ internal static class InputFeatureNames public const string kUseReadValueCaching = "USE_READ_VALUE_CACHING"; public const string kParanoidReadValueCachingChecks = "PARANOID_READ_VALUE_CACHING_CHECKS"; -#if UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR - public const string kUseUIToolkitEditor = "USE_UITOOLKIT_EDITOR"; +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + public const string kUseUIToolkitEditorForAllAssets = "USE_UITOOLKIT_EDITOR"; #endif } } diff --git a/Packages/com.unity.inputsystem/InputSystem/InputManagerStateMonitors.cs b/Packages/com.unity.inputsystem/InputSystem/InputManagerStateMonitors.cs index 1b83ab4f68..a550ed6f2a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputManagerStateMonitors.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputManagerStateMonitors.cs @@ -14,7 +14,7 @@ internal partial class InputManager ////TODO: support combining monitors for bitfields public void AddStateChangeMonitor(InputControl control, IInputStateChangeMonitor monitor, long monitorIndex, uint groupIndex) { - Debug.Assert(m_DevicesCount > 0); + if (m_DevicesCount <= 0) return; var device = control.device; var deviceIndex = device.m_DeviceIndex; diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs index 88cfbc52cf..7631760fef 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs @@ -3009,6 +3009,67 @@ internal static bool ShouldDrawWarningIconForBinding(string bindingPath) #region Actions +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + + private static InputActionAsset s_projectWideActions; + + /// + /// An input action asset (see ) which is always available by default. + /// + /// + /// A default set of actions and action maps are installed and enabled by default on every project. + /// These actions and their bindings may be modified in the Project Settings. + /// + /// + /// + /// + public static InputActionAsset actions + { + get + { + if (s_projectWideActions != null) + return s_projectWideActions; + + #if UNITY_EDITOR + // Load the InputActionsAsset and store it in EditorBuildSettings so it can be packed in Player builds + s_projectWideActions = Editor.ProjectWideActionsAsset.GetOrCreate(); + if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(s_projectWideActions))) + { + EditorBuildSettings.AddConfigObject(InputSettingsProvider.kEditorBuildSettingsActionsConfigKey, + s_projectWideActions, true); + } + #else + s_projectWideActions = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + #endif + + if (s_projectWideActions == null) + Debug.LogError($"Couldn't initialize project-wide input actions"); + return s_projectWideActions; + } + + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + if (s_projectWideActions == value) + return; + + #if UNITY_EDITOR + if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(value))) + { + EditorBuildSettings.AddConfigObject(InputSettingsProvider.kEditorBuildSettingsActionsConfigKey, + value, true); + } + #endif + + s_projectWideActions?.Disable(); + s_projectWideActions = value; + s_projectWideActions.Enable(); + } + } +#endif + /// /// Event that is signalled when the state of enabled actions in the system changes or /// when actions are triggered. @@ -3603,6 +3664,12 @@ private static void InitializeInPlayer(IInputRuntime runtime = null, InputSettin if (ShouldEnableRemoting()) SetUpRemoting(); #endif + +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && !UNITY_INCLUDE_TESTS + // Touching the `actions` property here will initialise it here (if it wasn't already). + // This is the point where we initialise project-wide actions for the Player + actions?.Enable(); +#endif } #endif // UNITY_EDITOR @@ -3690,6 +3757,16 @@ private static void Reset(bool enableRemoting = false, IInputRuntime runtime = n { Profiler.BeginSample("InputSystem.Reset"); +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Avoid touching the `actions` property directly here, to prevent unwanted initialisation. + if (s_projectWideActions) + { + s_projectWideActions.Disable(); + s_projectWideActions?.OnSetupChanged(); // Cleanup ActionState (remove unused controls after disabling) + s_projectWideActions = null; + } +#endif + // Some devices keep globals. Get rid of them by pretending the devices // are removed. if (s_Manager != null) @@ -3730,6 +3807,13 @@ private static void Reset(bool enableRemoting = false, IInputRuntime runtime = n InputEventListener.s_ObserverState = default; InputUser.ResetGlobals(); EnhancedTouchSupport.Reset(); + +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + // Touching the `actions` property will initialise it here (if it wasn't already). + // This is the point where we initialise project-wide actions for the Editor, Editor Tests and Player Tests. + actions?.Enable(); +#endif + Profiler.EndSample(); } @@ -3744,7 +3828,6 @@ private static void Destroy() // NOTE: Does not destroy InputSystemObject. We want to destroy input system // state repeatedly during tests but we want to not create InputSystemObject // over and over. - s_Manager.Destroy(); if (s_RemoteConnection != null) Object.DestroyImmediate(s_RemoteConnection); diff --git a/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef b/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef index 7b94feeab7..94c020739e 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef +++ b/Packages/com.unity.inputsystem/InputSystem/Unity.InputSystem.asmdef @@ -1,5 +1,6 @@ { "name": "Unity.InputSystem", + "rootNamespace": "", "references": [ "Unity.ugui" ], @@ -70,6 +71,11 @@ "name": "Unity", "expression": "2022.2", "define": "HAS_SET_LOCAL_POSITION_AND_ROTATION" + }, + { + "name": "Unity", + "expression": "2022.3", + "define": "UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS" } ], "noEngineReferences": false From 1fdfa3c0aa3ed585011fa2f7c3be59d7535c2d99 Mon Sep 17 00:00:00 2001 From: Jak Boulton Date: Tue, 29 Aug 2023 14:42:22 +0100 Subject: [PATCH 10/14] FIX: Swapped AdvancedDropdownGUI.toolbarSearchField to use EditorStyles directly (#1730) Co-authored-by: James McGill --- .../Editor/Internal/AdvancedDropdown/AdvancedDropdownGUI.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/AdvancedDropdown/AdvancedDropdownGUI.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/AdvancedDropdown/AdvancedDropdownGUI.cs index 46520144f9..0f2a0c0c90 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/AdvancedDropdown/AdvancedDropdownGUI.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/Internal/AdvancedDropdown/AdvancedDropdownGUI.cs @@ -10,11 +10,7 @@ internal class AdvancedDropdownGUI { private static class Styles { -#if UNITY_2023_2_OR_NEWER || UNITY_2021_3_28 || UNITY_2022_3_1 - public static readonly GUIStyle toolbarSearchField = "ToolbarSearchTextField"; -#else - public static readonly GUIStyle toolbarSearchField = "ToolbarSeachTextField"; -#endif + public static readonly GUIStyle toolbarSearchField = EditorStyles.toolbarSearchField; public static readonly GUIStyle itemStyle = new GUIStyle("PR Label") .WithAlignment(TextAnchor.MiddleLeft) .WithPadding(new RectOffset()) From 81958b23a4679fec2ad7090ab944d5d48c0891bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= Date: Tue, 29 Aug 2023 17:11:00 +0200 Subject: [PATCH 11/14] FIX: Renaming a control scheme doesn't correct the bindings to come with it (ISX-1567) (#1740) * Rename bindings control scheme's when control scheme is renamed Also refactors this renaming into a helper method. * Select the updated control scheme or the new one saved This fixes one failing test for the Editor --- .../Commands/ControlSchemeCommands.cs | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs index d60cf1b624..4d7e87f5c1 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/Commands/ControlSchemeCommands.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using UnityEditor; using UnityEngine.InputSystem.Utilities; namespace UnityEngine.InputSystem.Editor @@ -32,7 +33,7 @@ public static Command RemoveDeviceRequirement(int selectedDeviceIndex) }; } - public static Command SaveControlScheme(string newName = "", bool updateExisting = false) + public static Command SaveControlScheme(string newControlSchemeName = "", bool updateExisting = false) { return (in InputActionsEditorState state) => { @@ -42,7 +43,9 @@ public static Command SaveControlScheme(string newName = "", bool updateExisting var controlScheme = controlSchemesArray .FirstOrDefault(sp => sp.FindPropertyRelative(nameof(InputControlScheme.m_Name)).stringValue == controlSchemeName); - // if the control scheme is null, we're saving a new control scheme, otherwise editing an existing one + var actionMaps = state.serializedObject.FindProperty(nameof(InputActionAsset.m_ActionMaps)); + + // If the control scheme is null, we're saving a new control scheme, otherwise editing an existing one if (controlScheme == null && updateExisting) throw new InvalidOperationException("Tried to update a non-existent control scheme."); @@ -52,8 +55,15 @@ public static Command SaveControlScheme(string newName = "", bool updateExisting controlSchemesArray.InsertArrayElementAtIndex(controlSchemesArray.arraySize); controlScheme = controlSchemesArray.GetArrayElementAtIndex(controlSchemesArray.arraySize - 1); } + // If we're renaming a control scheme, we need to update the bindings that use it and make a unique name + if (!string.IsNullOrEmpty(newControlSchemeName)) + { + newControlSchemeName = MakeUniqueControlSchemeName(state, newControlSchemeName); + RenameBindingsControlSchemeHelper(controlScheme, actionMaps, controlSchemeName, newControlSchemeName); + } - controlScheme.FindPropertyRelative(nameof(InputControlScheme.m_Name)).stringValue = string.IsNullOrEmpty(newName) ? controlSchemeName : newName; + controlScheme.FindPropertyRelative(nameof(InputControlScheme.m_Name)).stringValue = string.IsNullOrEmpty(newControlSchemeName) ? controlSchemeName : newControlSchemeName; + controlScheme.FindPropertyRelative(nameof(InputControlScheme.m_BindingGroup)).stringValue = string.IsNullOrEmpty(newControlSchemeName) ? controlSchemeName : newControlSchemeName; var serializedDeviceRequirements = controlScheme.FindPropertyRelative(nameof(InputControlScheme.m_DeviceRequirements)); serializedDeviceRequirements.ClearArray(); @@ -71,10 +81,34 @@ public static Command SaveControlScheme(string newName = "", bool updateExisting } state.serializedObject.ApplyModifiedProperties(); - return state.With(selectedControlScheme: new InputControlScheme(controlScheme)); + return state.With( + selectedControlScheme: new InputControlScheme(controlScheme), + // Select the control scheme updated, otherwise select the new one it was added + selectedControlSchemeIndex: updateExisting? state.selectedControlSchemeIndex: controlSchemesArray.arraySize - 1); }; } + static void RenameBindingsControlSchemeHelper(SerializedProperty controlScheme, SerializedProperty actionMaps, string controlSchemeName, string newName) + { + foreach (SerializedProperty actionMap in actionMaps) + { + var bindings = actionMap + .FindPropertyRelative(nameof(InputActionMap.m_Bindings)) + .Select(sp => new SerializedInputBinding(sp)) + .ToList(); + + var bindingsToRename = bindings.Where(b => b.controlSchemes.Contains(controlSchemeName)).ToList(); + + foreach (var binding in bindingsToRename) + { + var bindingGroups = binding.controlSchemes.ToList(); + bindingGroups.Remove(controlSchemeName); + bindingGroups.Add(newName); + binding.wrappedProperty.FindPropertyRelative(nameof(InputBinding.m_Groups)).stringValue = bindingGroups.Join(InputBinding.kSeparatorString); + } + } + } + public static Command SelectControlScheme(int controlSchemeIndex) { return (in InputActionsEditorState state) => From a7763b3a8e4b72c98741153aec3d8174db1af1a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o?= Date: Tue, 29 Aug 2023 18:03:46 +0200 Subject: [PATCH 12/14] CHANGE: Set minSize for UITK InputActionsEditorWindow (ISX-1565) (#1739) --- .../Editor/UITKAssetEditor/InputActionsEditorWindow.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs index 57b58db415..412ae0e2c9 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/UITKAssetEditor/InputActionsEditorWindow.cs @@ -32,6 +32,7 @@ internal class InputActionsEditorWindow : EditorWindow private string m_AssetPath; private string m_AssetJson; private bool m_IsDirty; + static readonly Vector2 k_MinWindowSize = new Vector2(650, 450); [OnOpenAsset] public static bool OpenAsset(int instanceId, int line) @@ -61,6 +62,7 @@ public static bool OpenAsset(int instanceId, int line) window.m_IsDirty = false; window.m_AssetId = instanceId; window.titleContent = new GUIContent("Input Actions Editor"); + window.minSize = k_MinWindowSize; window.SetAsset(asset); window.Show(); From 665d11d8391562582f90d9d1dabb9ebbf0e20d47 Mon Sep 17 00:00:00 2001 From: James McGill Date: Wed, 30 Aug 2023 14:03:14 +0200 Subject: [PATCH 13/14] NEW: Add tests for Project wide actions (#1742) * project-wide actions: add missing control schemes to some bindings * ensure we only load the InputActionsAsset containing the projectwide actions at runtime * add some tests for project wide settings * Temporarily disable some tests in Player which have issues with ProjectWide actions * run code formatter * review comments --- Assets/Tests/InputSystem/CoreTests_Actions.cs | 2 +- Assets/Tests/InputSystem/CoreTests_Devices.cs | 4 +- .../CoreTests_ProjectWideActions.cs | 215 ++++++++++++++++++ .../CoreTests_ProjectWideActions.cs.meta | 11 + Assets/Tests/InputSystem/TestActionsAsset.cs | 5 + .../InputSystem/TestActionsAsset.cs.meta | 11 + .../ProjectWideActionsAsset.cs | 12 +- .../ProjectWideActionsTemplate.inputactions | 8 +- .../InputSystem/InputSystem.cs | 3 +- 9 files changed, 261 insertions(+), 10 deletions(-) create mode 100644 Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs create mode 100644 Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs.meta create mode 100644 Assets/Tests/InputSystem/TestActionsAsset.cs create mode 100644 Assets/Tests/InputSystem/TestActionsAsset.cs.meta diff --git a/Assets/Tests/InputSystem/CoreTests_Actions.cs b/Assets/Tests/InputSystem/CoreTests_Actions.cs index 2932b578ee..22c7c73776 100644 --- a/Assets/Tests/InputSystem/CoreTests_Actions.cs +++ b/Assets/Tests/InputSystem/CoreTests_Actions.cs @@ -5020,7 +5020,7 @@ public void Actions_WhenControlsUpdate_TimeoutsAreCarriedOver() [Category("Actions")] public void Actions_WhenControlsUpdate_InProgressActionsKeepGoing() { -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && UNITY_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455) // Causes: "[Assert] Could not find active control after binding resolution" // when RemoveDevice() called diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 3b6e9ed063..b963996fb1 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -4390,7 +4390,7 @@ public void TODO_Devices_CanHijackEventStreamOfDevice() [TestCase(false, InputSettings.BackgroundBehavior.ResetAndDisableNonBackgroundDevices, InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView)] public unsafe void Devices_CanHandleFocusChanges(bool appRunInBackground, InputSettings.BackgroundBehavior backgroundBehavior, InputSettings.EditorInputBehaviorInPlayMode editorInputBehaviorInPlayMode) { -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && UNITY_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455) // Causes: "[Assert] Could not find active control after binding resolution" // due to: mouse3 = InputSystem.AddDevice(); @@ -5363,7 +5363,7 @@ public void Devices_RemovingKeyboardMakesNextKeyboardCurrent() [Category("Devices")] public void Devices_RemovingDevice_MakesNextDeviceOfTypeCurrent() { -#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS && UNITY_EDITOR +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // @TODO: This should not need disabled for this test (https://jira.unity3d.com/browse/ISX-1455) // Causes: "[Assert] Could not find active control after binding resolution" // during point where Pointer is removed diff --git a/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs new file mode 100644 index 0000000000..097d7bead6 --- /dev/null +++ b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs @@ -0,0 +1,215 @@ +#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS + +using System; +using System.IO; +using NUnit.Framework; +using UnityEditor; +using UnityEngine; +using UnityEngine.InputSystem; + +#if UNITY_EDITOR +using UnityEngine.InputSystem.Editor; +#endif + +internal partial class CoreTests +{ + const string TestCategory = "ProjectWideActions"; + const string TestAssetPath = "Assets/TestInputManager.asset"; + string m_TemplateAssetPath; + +#if UNITY_EDITOR + const int initialActionCount = 2; + const int initialMapCount = 1; +#else + const int initialActionCount = 17; + const int initialMapCount = 2; +#endif + + [SetUp] + public override void Setup() + { + // @TODO: Currently we can only inject the TestActionsAsset in PlayMode tests. + // It would be nice to be able to inject it as a Preloaded asset into the Player tests so + // we don't need different tests for the player. + // This also means these tests are dependant on the content of InputManager.asset not being changed. +#if UNITY_EDITOR + // This asset takes the place of ProjectSettings/InputManager.asset for the sake of testing, as we don't + // really want to go changing that asset in every test. + // This is used as a backing for `InputSystem.actions` in PlayMode tests. + var testAsset = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(testAsset, TestAssetPath); + + // Create a template `InputActionAsset` containing some test actions. + // This will then be used to populate the initially empty `TestActionsAsset` when it is first acessed. + var templateActions = ScriptableObject.CreateInstance(); + templateActions.name = "TestAsset"; + var map = templateActions.AddActionMap("InitialActionMapOne"); + map.AddAction("InitialActionOne"); + map.AddAction("InitialActionTwo"); + + m_TemplateAssetPath = Path.Combine(Environment.CurrentDirectory, "Assets/ProjectWideActionsTemplate.inputactions"); + File.WriteAllText(m_TemplateAssetPath, templateActions.ToJson()); + + ProjectWideActionsAsset.SetAssetPaths(m_TemplateAssetPath, TestAssetPath); +#endif + + base.Setup(); + } + + [TearDown] + public override void TearDown() + { +#if UNITY_EDITOR + ProjectWideActionsAsset.Reset(); + + if (File.Exists(m_TemplateAssetPath)) + File.Delete(m_TemplateAssetPath); + + AssetDatabase.DeleteAsset(TestAssetPath); +#endif + + base.TearDown(); + } + +#if UNITY_EDITOR + [Test] + [Category(TestCategory)] + public void ProjectWideActionsAsset_TemplateAssetIsInstalledOnFirstUse() + { + var asset = ProjectWideActionsAsset.GetOrCreate(); + + Assert.That(asset, Is.Not.Null); + Assert.That(asset.actionMaps.Count, Is.EqualTo(initialMapCount)); + Assert.That(asset.actionMaps[0].actions.Count, Is.EqualTo(initialActionCount)); + Assert.That(asset.actionMaps[0].actions[0].name, Is.EqualTo("InitialActionOne")); + } + + [Test] + [Category(TestCategory)] + public void ProjectWideActionsAsset_CanModifySaveAndLoadAsset() + { + var asset = ProjectWideActionsAsset.GetOrCreate(); + + Assert.That(asset, Is.Not.Null); + Assert.That(asset.actionMaps.Count, Is.EqualTo(initialMapCount)); + Assert.That(asset.actionMaps[0].actions.Count, Is.EqualTo(initialActionCount)); + Assert.That(asset.actionMaps[0].actions[0].name, Is.EqualTo("InitialActionOne")); + + asset.Disable(); // Cannot modify active actions + + // Add more actions + asset.actionMaps[0].AddAction("ActionTwo"); + asset.actionMaps[0].AddAction("ActionThree"); + + // Modify existing + asset.actionMaps[0].actions[0].Rename("FirstAction"); + + // Add another map + asset.AddActionMap("ActionMapTwo").AddAction("AnotherAction"); + + // Save + AssetDatabase.SaveAssets(); + + // Reload + asset = ProjectWideActionsAsset.GetOrCreate(); + + Assert.That(asset, Is.Not.Null); + Assert.That(asset.actionMaps.Count, Is.EqualTo(initialMapCount + 1)); + Assert.That(asset.actionMaps[0].actions.Count, Is.EqualTo(initialActionCount + 2)); + Assert.That(asset.actionMaps[1].actions.Count, Is.EqualTo(1)); + Assert.That(asset.actionMaps[0].actions[0].name, Is.EqualTo("FirstAction")); + Assert.That(asset.actionMaps[1].actions[0].name, Is.EqualTo("AnotherAction")); + } + +#endif + + [Test] + [Category(TestCategory)] + public void ProjectWideActions_AreEnabledByDefault() + { + Assert.That(InputSystem.actions, Is.Not.Null); + Assert.That(InputSystem.actions.enabled, Is.True); + } + + [Test] + [Category(TestCategory)] + public void ProjectWideActions_ContainsTemplateActions() + { + Assert.That(InputSystem.actions, Is.Not.Null); + Assert.That(InputSystem.actions.actionMaps.Count, Is.EqualTo(initialMapCount)); + +#if UNITY_EDITOR + Assert.That(InputSystem.actions.actionMaps[0].actions.Count, Is.EqualTo(initialActionCount)); + Assert.That(InputSystem.actions.actionMaps[0].actions[0].name, Is.EqualTo("InitialActionOne")); +#else + Assert.That(InputSystem.actions.actionMaps[0].actions.Count, Is.EqualTo(9)); + Assert.That(InputSystem.actions.actionMaps[0].actions[0].name, Is.EqualTo("Move")); +#endif + } + + [Test] + [Category(TestCategory)] + public void ProjectWideActions_AppearInEnabledActions() + { + var enabledActions = InputSystem.ListEnabledActions(); + Assert.That(enabledActions, Has.Count.EqualTo(initialActionCount)); + + // Add more actions also work + var action = new InputAction(name: "standaloneAction"); + action.Enable(); + + enabledActions = InputSystem.ListEnabledActions(); + Assert.That(enabledActions, Has.Count.EqualTo(initialActionCount + 1)); + Assert.That(enabledActions, Has.Exactly(1).SameAs(action)); + + // Disabling works + InputSystem.actions.Disable(); + enabledActions = InputSystem.ListEnabledActions(); + Assert.That(enabledActions, Has.Count.EqualTo(1)); + Assert.That(enabledActions, Has.Exactly(1).SameAs(action)); + } + + [Test] + [Category(TestCategory)] + public void ProjectWideActions_CanReplaceExistingActions() + { + // Initial State + Assert.That(InputSystem.actions, Is.Not.Null); + Assert.That(InputSystem.actions.enabled, Is.True); + var enabledActions = InputSystem.ListEnabledActions(); + Assert.That(enabledActions, Has.Count.EqualTo(initialActionCount)); + + // Build new asset + var asset = ScriptableObject.CreateInstance(); + var map1 = new InputActionMap("replacedMap1"); + var map2 = new InputActionMap("replacedMap2"); + var action1 = map1.AddAction("replacedAction1", InputActionType.Button); + var action2 = map1.AddAction("replacedAction2", InputActionType.Button); + var action3 = map1.AddAction("replacedAction3", InputActionType.Button); + var action4 = map2.AddAction("replacedAction4", InputActionType.Button); + + action1.AddBinding("/buttonSouth"); + action2.AddBinding("/buttonWest"); + action3.AddBinding("/buttonNorth"); + action4.AddBinding("/buttonEast"); + asset.AddActionMap(map1); + asset.AddActionMap(map2); + + // Replace project-wide actions + InputSystem.actions = asset; + + // State after replacing + Assert.That(InputSystem.actions, Is.Not.Null); + Assert.That(InputSystem.actions.enabled, Is.True); + enabledActions = InputSystem.ListEnabledActions(); + Assert.That(enabledActions, Has.Count.EqualTo(4)); + + Assert.That(InputSystem.actions.actionMaps.Count, Is.EqualTo(2)); + Assert.That(InputSystem.actions.actionMaps[0].actions.Count, Is.EqualTo(3)); + Assert.That(InputSystem.actions.actionMaps[0].actions[0].name, Is.EqualTo("replacedAction1")); + Assert.That(InputSystem.actions.actionMaps[1].actions.Count, Is.EqualTo(1)); + Assert.That(InputSystem.actions.actionMaps[1].actions[0].name, Is.EqualTo("replacedAction4")); + } +} + +#endif diff --git a/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs.meta b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs.meta new file mode 100644 index 0000000000..e1b8a266a6 --- /dev/null +++ b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 52e6b4a92448f524d99a9465f6cc8b54 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/InputSystem/TestActionsAsset.cs b/Assets/Tests/InputSystem/TestActionsAsset.cs new file mode 100644 index 0000000000..70e1fd9dfb --- /dev/null +++ b/Assets/Tests/InputSystem/TestActionsAsset.cs @@ -0,0 +1,5 @@ +using UnityEngine; + +internal class TestActionsAsset : ScriptableObject +{ +} diff --git a/Assets/Tests/InputSystem/TestActionsAsset.cs.meta b/Assets/Tests/InputSystem/TestActionsAsset.cs.meta new file mode 100644 index 0000000000..90b00f3740 --- /dev/null +++ b/Assets/Tests/InputSystem/TestActionsAsset.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91991318f6d19a74b9ac916df855fc69 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs index 6660a57a81..c2b6d1b54f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs @@ -12,18 +12,26 @@ internal static class ProjectWideActionsAsset { internal const string kDefaultAssetPath = "Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions"; internal const string kAssetPath = "ProjectSettings/InputManager.asset"; - internal const string kAssetName = "ProjectWideInputActions"; + internal const string kAssetName = InputSystem.kProjectWideActionsAssetName; static string s_DefaultAssetPath = kDefaultAssetPath; static string s_AssetPath = kAssetPath; - // For Testing +#if UNITY_INCLUDE_TESTS internal static void SetAssetPaths(string defaultAssetPath, string assetPath) { s_DefaultAssetPath = defaultAssetPath; s_AssetPath = assetPath; } + internal static void Reset() + { + s_DefaultAssetPath = kDefaultAssetPath; + s_AssetPath = kAssetPath; + } + +#endif + [InitializeOnLoadMethod] internal static void InstallProjectWideActions() { diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions index 31333c2493..5d4dbca6ae 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions +++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsTemplate.inputactions @@ -314,7 +314,7 @@ "path": "/enter", "interactions": "", "processors": "", - "groups": "", + "groups": "Keyboard&Mouse", "action": "Attack", "isComposite": false, "isPartOfComposite": false @@ -347,7 +347,7 @@ "path": "/leftShift", "interactions": "", "processors": "", - "groups": "", + "groups": "Keyboard&Mouse", "action": "Sprint", "isComposite": false, "isPartOfComposite": false @@ -369,7 +369,7 @@ "path": "/trigger", "interactions": "", "processors": "", - "groups": "", + "groups": "XR", "action": "Sprint", "isComposite": false, "isPartOfComposite": false @@ -446,7 +446,7 @@ "path": "/buttonNorth", "interactions": "", "processors": "", - "groups": "", + "groups": "Gamepad", "action": "Interact", "isComposite": false, "isPartOfComposite": false diff --git a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs index 7631760fef..c9c31911ef 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputSystem.cs @@ -3012,6 +3012,7 @@ internal static bool ShouldDrawWarningIconForBinding(string bindingPath) #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS private static InputActionAsset s_projectWideActions; + internal const string kProjectWideActionsAssetName = "ProjectWideInputActions"; /// /// An input action asset (see ) which is always available by default. @@ -3039,7 +3040,7 @@ public static InputActionAsset actions s_projectWideActions, true); } #else - s_projectWideActions = Resources.FindObjectsOfTypeAll().FirstOrDefault(); + s_projectWideActions = Resources.FindObjectsOfTypeAll().FirstOrDefault(o => o != null && o.name == kProjectWideActionsAssetName); #endif if (s_projectWideActions == null) From e25e307515068c8bc62b15c0bd6550a568285865 Mon Sep 17 00:00:00 2001 From: Stefan Schubert Date: Mon, 4 Sep 2023 13:05:00 +0200 Subject: [PATCH 14/14] RELEASE: 1.8.0-pre.1 changelog changes for 1.8.0-pre.1 --- Packages/com.unity.inputsystem/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md index 6f915b7951..c95bb03604 100644 --- a/Packages/com.unity.inputsystem/CHANGELOG.md +++ b/Packages/com.unity.inputsystem/CHANGELOG.md @@ -8,11 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. Due to package verification, the latest version below is the unpublished version and the date is meaningless. however, it has to be formatted properly to pass verification tests. -## [Unreleased] +## [1.8.0-pre.1] - 2023-08-14 ### Added - Initial version of Project Wide Actions for pre-release (`InputSystem.actions`). This feature is available only on Unity Editor versions 2022.3 and above and can be modified in the Project Settings. +### Fixed +- Fixed device selection menu not responding to mouse clicks when trying to add a device in a Control Scheme ([case ISXB-622](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-622)). + ## [1.7.0] - 2023-08-14 ### Added