diff --git a/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs
index 89539950ff..5ead638bbc 100644
--- a/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs
+++ b/Assets/Tests/InputSystem/CoreTests_ProjectWideActions.cs
@@ -1,7 +1,9 @@
#if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
using System;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
diff --git a/Packages/com.unity.inputsystem/CHANGELOG.md b/Packages/com.unity.inputsystem/CHANGELOG.md
index 0cb8f32068..2f0a7c5b14 100644
--- a/Packages/com.unity.inputsystem/CHANGELOG.md
+++ b/Packages/com.unity.inputsystem/CHANGELOG.md
@@ -23,6 +23,7 @@ however, it has to be formatted properly to pass verification tests.
### Added
- Support for [Game rotation vector](https://developer.android.com/reference/android/hardware/Sensor#TYPE_GAME_ROTATION_VECTOR) sensor on Android
- Duplicate Input Action Items in the new Input Action Asset Editor with Ctrl+D (Windows) or Cmd+D (Mac)
+- Selection of InputActionReferences from project-wide actions on fields that are of type InputActionReference. Uses a new advanced object picker that allows better searching and filtering of actions.
### Fixed
- Partially fixed case ISX-1357 (Investigate performance regressing over time). A sample showed that leaving an InputActionMap enabled could lead to an internal list of listeners growing. This leads to slow-down, so we now warn if we think this is happening.
@@ -38,6 +39,7 @@ however, it has to be formatted properly to pass verification tests.
- Fixed case [ISX-1668] (The Profiler shows incorrect data and spams the console with "Missing Profiler.EndSample" errors when there is an Input System Component in Scene).
- Fixed [ISX-1661](https://jira.unity3d.com/browse/ISX-1661) where undoing duplications of action maps caused console errors
- Fix for BindingSyntax `WithInteraction()` which was incorrectly using processors.
+- Fixed issue of visual elements being null during editing project-wide actions in project settings which prompted console errors.
## [1.8.0-pre.1] - 2023-09-04
diff --git a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionReference.cs b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionReference.cs
index 0090f46bb8..73a73cd68e 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionReference.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/Actions/InputActionReference.cs
@@ -160,7 +160,7 @@ public override string ToString()
return base.ToString();
}
- private static string GetDisplayName(InputAction action)
+ internal static string GetDisplayName(InputAction action)
{
return !string.IsNullOrEmpty(action?.actionMap?.name) ? $"{action.actionMap?.name}/{action.name}" : action?.name;
}
diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionAssetIconLoader.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionAssetIconLoader.cs
new file mode 100644
index 0000000000..6e6ad8a9e3
--- /dev/null
+++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionAssetIconLoader.cs
@@ -0,0 +1,35 @@
+#if UNITY_EDITOR
+using UnityEditor;
+
+namespace UnityEngine.InputSystem.Editor
+{
+ ///
+ /// Provides access to icons associated with and .
+ ///
+ internal static class InputActionAssetIconLoader
+ {
+ private const string kActionIcon = "Packages/com.unity.inputsystem/InputSystem/Editor/Icons/InputAction.png";
+ private const string kAssetIcon = "Packages/com.unity.inputsystem/InputSystem/Editor/Icons/InputActionAsset.png";
+
+ ///
+ /// Attempts to load the icon associated with an .
+ ///
+ /// Icon resource reference or null if the resource could not be loaded.
+ internal static Texture2D LoadAssetIcon()
+ {
+ return (Texture2D)EditorGUIUtility.Load(kAssetIcon);
+ }
+
+ ///
+ /// Attempts to load the icon associated with an sub-asset of an
+ /// .
+ ///
+ /// Icon resource reference or null if the resource could not be loaded.
+ internal static Texture2D LoadActionIcon()
+ {
+ return (Texture2D)EditorGUIUtility.Load(kActionIcon);
+ }
+ }
+}
+
+#endif // #if UNITY_EDITOR
diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionAssetIconLoader.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionAssetIconLoader.cs.meta
new file mode 100644
index 0000000000..2e1d7a85f4
--- /dev/null
+++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionAssetIconLoader.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 015a929bdae646a99b91b756b998233f
+timeCreated: 1698142059
\ No newline at end of file
diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporter.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporter.cs
index 5f45b6f02b..24d528e87d 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporter.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/Editor/AssetImporter/InputActionImporter.cs
@@ -1,5 +1,6 @@
#if UNITY_EDITOR
using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
@@ -29,9 +30,6 @@ internal class InputActionImporter : ScriptedImporter
{
private const int kVersion = 13;
- private const string kActionIcon = "Packages/com.unity.inputsystem/InputSystem/Editor/Icons/InputAction.png";
- private const string kAssetIcon = "Packages/com.unity.inputsystem/InputSystem/Editor/Icons/InputActionAsset.png";
-
[SerializeField] private bool m_GenerateWrapperCode;
[SerializeField] private string m_WrapperCodePath;
[SerializeField] private string m_WrapperClassName;
@@ -88,8 +86,8 @@ public override void OnImportAsset(AssetImportContext ctx)
// Load icons.
////REVIEW: the icons won't change if the user changes skin; not sure it makes sense to differentiate here
- var assetIcon = (Texture2D)EditorGUIUtility.Load(kAssetIcon);
- var actionIcon = (Texture2D)EditorGUIUtility.Load(kActionIcon);
+ var assetIcon = InputActionAssetIconLoader.LoadAssetIcon();
+ var actionIcon = InputActionAssetIconLoader.LoadActionIcon();
// Add asset.
ctx.AddObjectToAsset("", asset, assetIcon);
@@ -212,12 +210,51 @@ public override void OnImportAsset(AssetImportContext ctx)
InputActionEditorWindow.RefreshAllOnAssetReimport();
}
+ internal static IEnumerable LoadInputActionReferencesFromAsset(InputActionAsset asset)
+ {
+ //Get all InputActionReferences are stored at the same asset path as InputActionAsset
+ return AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(asset)).Where(
+ o => o is InputActionReference && o.name != "InputManager").Cast();
+ }
+
+ // Get all InputActionReferences from assets in the project. By default it only gets the assets in the "Assets" folder.
+ internal static IEnumerable LoadInputActionReferencesFromAssetDatabase(string[] foldersPath = null)
+ {
+ string[] searchFolders = null;
+ // If folderPath is null, search in "Assets" folder.
+ if (foldersPath == null)
+ {
+ searchFolders = new string[] { "Assets" };
+ }
+
+ // Get all InputActionReference from assets in "Asset" folder. It does not search inside "Packages" folder.
+ var inputActionReferenceGUIDs = AssetDatabase.FindAssets($"t:{typeof(InputActionReference).Name}", searchFolders);
+
+ // To find all the InputActionReferences, the GUID of the asset containing at least one action reference is
+ // used to find the asset path. This is because InputActionReferences are stored in the asset database as sub-assets of InputActionAsset.
+ // Then the whole asset is loaded and all the InputActionReferences are extracted from it.
+ // Also, the action references are duplicated to have backwards compatibility with the 1.0.0-preview.7. That
+ // is why we look for references withouth the `HideFlags.HideInHierarchy` flag.
+ var inputActionReferencesList = new List();
+ foreach (var guid in inputActionReferenceGUIDs)
+ {
+ var assetPath = AssetDatabase.GUIDToAssetPath(guid);
+ var assetInputActionReferenceList = AssetDatabase.LoadAllAssetsAtPath(assetPath).Where(
+ o => o is InputActionReference &&
+ !((InputActionReference)o).hideFlags.HasFlag(HideFlags.HideInHierarchy))
+ .Cast().ToList();
+
+ inputActionReferencesList.AddRange(assetInputActionReferenceList);
+ }
+ return inputActionReferencesList;
+ }
+
// Add item to plop an .inputactions asset into the project.
[MenuItem("Assets/Create/Input Actions")]
public static void CreateInputAsset()
{
ProjectWindowUtil.CreateAssetWithContent("New Controls." + InputActionAsset.Extension,
- InputActionAsset.kDefaultAssetLayoutJson, (Texture2D)EditorGUIUtility.Load(kAssetIcon));
+ InputActionAsset.kDefaultAssetLayoutJson, InputActionAssetIconLoader.LoadAssetIcon());
}
}
}
diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs
index 68146646f1..b8646565c9 100644
--- a/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs
+++ b/Packages/com.unity.inputsystem/InputSystem/Editor/ProjectWideActions/ProjectWideActionsAsset.cs
@@ -86,8 +86,16 @@ private static InputActionAsset CreateNewActionAsset()
}
}
- // 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.
+ CreateInputActionReferences(asset);
+
+ AssetDatabase.SaveAssets();
+
+ return asset;
+ }
+
+ private static void CreateInputActionReferences(InputActionAsset asset)
+ {
+ var maps = asset.actionMaps;
foreach (var map in maps)
{
foreach (var action in map.actions)
@@ -97,10 +105,51 @@ private static InputActionAsset CreateNewActionAsset()
AssetDatabase.AddObjectToAsset(actionReference, asset);
}
}
+ }
- AssetDatabase.SaveAssets();
+ ///
+ /// Updates the input action references in the asset by updating names, removing dangling references
+ /// and adding new ones.
+ ///
+ ///
+ internal static void UpdateInputActionReferences()
+ {
+ var asset = GetOrCreate();
+ var existingReferences = InputActionImporter.LoadInputActionReferencesFromAsset(asset).ToList();
- return asset;
+ // Check if referenced input action exists in the asset and remove the reference if it doesn't.
+ foreach (var actionReference in existingReferences)
+ {
+ var action = asset.FindAction(actionReference.action.id);
+ if (action == null)
+ {
+ actionReference.Set(null);
+ AssetDatabase.RemoveObjectFromAsset(actionReference);
+ }
+ }
+
+ // Check if all actions have a reference
+ foreach (var action in asset)
+ {
+ var actionReference = existingReferences.FirstOrDefault(r => r.m_ActionId == action.id.ToString());
+ // The input action doesn't have a reference, create a new one.
+ if (actionReference == null)
+ {
+ var actionReferenceNew = ScriptableObject.CreateInstance();
+ actionReferenceNew.Set(action);
+ AssetDatabase.AddObjectToAsset(actionReferenceNew, asset);
+ }
+ else
+ {
+ // Update the name of the reference if it doesn't match the action name.
+ if (actionReference.name != InputActionReference.GetDisplayName(action))
+ {
+ AssetDatabase.RemoveObjectFromAsset(actionReference);
+ actionReference.name = InputActionReference.GetDisplayName(action);
+ AssetDatabase.AddObjectToAsset(actionReference, asset);
+ }
+ }
+ }
}
}
}
diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/PropertyDrawers/InputActionReferencePropertyDrawer.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/PropertyDrawers/InputActionReferencePropertyDrawer.cs
new file mode 100644
index 0000000000..3186315b9f
--- /dev/null
+++ b/Packages/com.unity.inputsystem/InputSystem/Editor/PropertyDrawers/InputActionReferencePropertyDrawer.cs
@@ -0,0 +1,51 @@
+// Note: If not UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS we do not use a custom property drawer and
+// picker for InputActionReferences but rather rely on default (classic) object picker.
+#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
+
+using UnityEditor;
+using UnityEditor.Search;
+
+namespace UnityEngine.InputSystem.Editor
+{
+ ///
+ /// Custom property drawer in order to use the "Advanced Picker" from UnityEditor.Search.
+ ///
+ [CustomPropertyDrawer(typeof(InputActionReference))]
+ internal sealed class InputActionReferencePropertyDrawer : PropertyDrawer
+ {
+ private readonly SearchContext m_Context = UnityEditor.Search.SearchService.CreateContext(new[]
+ {
+ InputActionReferenceSearchProviders.CreateInputActionReferenceSearchProviderForAssets(),
+ InputActionReferenceSearchProviders.CreateInputActionReferenceSearchProviderForProjectWideActions(),
+ }, string.Empty, SearchConstants.PickerSearchFlags);
+
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ // Sets the property to null if the action is not found in the asset.
+ ValidatePropertyWithDanglingInputActionReferences(property);
+
+ ObjectField.DoObjectField(position, property, typeof(InputActionReference), label,
+ m_Context, SearchConstants.PickerViewFlags);
+ }
+
+ static void ValidatePropertyWithDanglingInputActionReferences(SerializedProperty property)
+ {
+ if (property?.objectReferenceValue is InputActionReference reference)
+ {
+ // Check only if the reference is a project-wide action.
+ if (reference?.asset?.name == ProjectWideActionsAsset.kAssetName)
+ {
+ var action = reference?.asset?.FindAction(reference.action.id);
+ if (action is null)
+ {
+ property.objectReferenceValue = null;
+ property.serializedObject.ApplyModifiedProperties();
+ }
+ }
+ }
+ }
+ }
+}
+
+#endif
diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/PropertyDrawers/InputActionReferencePropertyDrawer.cs.meta b/Packages/com.unity.inputsystem/InputSystem/Editor/PropertyDrawers/InputActionReferencePropertyDrawer.cs.meta
new file mode 100644
index 0000000000..6a705fdc38
--- /dev/null
+++ b/Packages/com.unity.inputsystem/InputSystem/Editor/PropertyDrawers/InputActionReferencePropertyDrawer.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 30bd04fe045c46918b5fe7bc6efc4ce2
+timeCreated: 1698143402
\ No newline at end of file
diff --git a/Packages/com.unity.inputsystem/InputSystem/Editor/PropertyDrawers/InputActionReferenceSearchProviders.cs b/Packages/com.unity.inputsystem/InputSystem/Editor/PropertyDrawers/InputActionReferenceSearchProviders.cs
new file mode 100644
index 0000000000..8830de0a41
--- /dev/null
+++ b/Packages/com.unity.inputsystem/InputSystem/Editor/PropertyDrawers/InputActionReferenceSearchProviders.cs
@@ -0,0 +1,98 @@
+#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
+using System;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEditor.Search;
+using UnityEngine.Search;
+
+namespace UnityEngine.InputSystem.Editor
+{
+ internal static class SearchConstants
+ {
+ // SearchFlags: these flags are used to customize how search is performed and how search
+ // results are displayed in the advanced object picker.
+ // Note: SearchFlags.Packages is not currently used and hides all results from packages.
+ internal static readonly SearchFlags PickerSearchFlags = SearchFlags.Sorted | SearchFlags.OpenPicker;
+
+ // Search.SearchViewFlags : these flags are used to customize the appearance of the PickerWindow.
+ internal static readonly Search.SearchViewFlags PickerViewFlags = SearchViewFlags.DisableBuilderModeToggle
+ | SearchViewFlags.DisableInspectorPreview
+ | SearchViewFlags.ListView
+ | SearchViewFlags.DisableSavedSearchQuery;
+ }
+
+ internal static class InputActionReferenceSearchProviders
+ {
+ const string k_AssetFolderSearchProviderId = "AssetsInputActionReferenceSearchProvider";
+ const string k_ProjectWideActionsSearchProviderId = "ProjectWideInputActionReferenceSearchProvider";
+
+ // Search provider for InputActionReferences for all assets in the project, without project-wide actions.
+ internal static SearchProvider CreateInputActionReferenceSearchProviderForAssets()
+ {
+ return CreateInputActionReferenceSearchProvider(k_AssetFolderSearchProviderId,
+ "Asset Input Actions",
+ // Show the asset path in the description.
+ (obj) => AssetDatabase.GetAssetPath((obj as InputActionReference).asset),
+ () => InputActionImporter.LoadInputActionReferencesFromAssetDatabase());
+ }
+
+ // Search provider for InputActionReferences for project-wide actions
+ internal static SearchProvider CreateInputActionReferenceSearchProviderForProjectWideActions()
+ {
+ return CreateInputActionReferenceSearchProvider(k_ProjectWideActionsSearchProviderId,
+ "Project-Wide Input Actions",
+ (obj) => "(Project-Wide Input Actions)",
+ () => InputActionImporter.LoadInputActionReferencesFromAsset(ProjectWideActionsAsset.GetOrCreate()));
+ }
+
+ private static SearchProvider CreateInputActionReferenceSearchProvider(string id, string displayName,
+ Func