Skip to content

Commit ab2c3d7

Browse files
authored
FIX: Errors are thrown when assigning Input Actions Asset that contains a custom InputProcessor or InputBindingComposite as Project-wide Input Actions (ISXB-856) (#1958)
* Add reflection to load custom InputProcessor, IInputInteraction and InputBindingComposite
1 parent cf28d7d commit ab2c3d7

6 files changed

Lines changed: 79 additions & 53 deletions

File tree

Assets/Samples/CustomComposite/CustomComposite.cs

Lines changed: 13 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -27,61 +27,29 @@
2727
// our composite will not be shown as our value type (Vector2) is
2828
// incompatible with the value type of Axis (float).
2929
//
30-
// Also, we need to register our composite with the input system. And we
31-
// want to do it in a way that makes the composite visible in the action
32-
// editor of the input system.
33-
//
34-
// For that to happen, we need to call InputSystem.RegisterBindingComposite
35-
// sometime during startup. We make that happen by using [InitializeOnLoad]
36-
// in the editor and [RuntimeInitializeOnLoadMethod] in the player.
37-
#if UNITY_EDITOR
38-
[InitializeOnLoad]
39-
#endif
4030
// We can customize the way display strings are formed for our composite by
4131
// annotating it with DisplayStringFormatAttribute. The string is simply a
4232
// list with elements to be replaced enclosed in curly braces. Everything
4333
// outside those will taken verbatim. The fragments inside the curly braces
4434
// in this case refer to the binding composite parts by name. Each such
4535
// instance is replaced with the display text for the corresponding
4636
// part binding.
37+
//
38+
// NOTE: We don't supply a name for the composite here. The default logic
39+
// will take the name of the type ("CustomComposite" in our case)
40+
// and snip off "Composite" if used as a suffix (which is the case
41+
// for us) and then use that as the name. So in our case, we are
42+
// registering a composite called "Custom" here.
43+
//
44+
// If we were to use our composite with the AddCompositeBinding API,
45+
// for example, it would look like this:
46+
//
47+
// myAction.AddCompositeBinding("Custom")
48+
// .With("Stick", "<Gamepad>/leftStick")
49+
// .With("Multiplier", "<Gamepad>/rightTrigger");
4750
[DisplayStringFormat("{multiplier}*{stick}")]
4851
public class CustomComposite : InputBindingComposite<Vector2>
4952
{
50-
// In the editor, the static class constructor will be called on startup
51-
// because of [InitializeOnLoad].
52-
#if UNITY_EDITOR
53-
static CustomComposite()
54-
{
55-
// Trigger our RegisterBindingComposite code in the editor.
56-
Initialize();
57-
}
58-
59-
#endif
60-
61-
// In the player, [RuntimeInitializeOnLoadMethod] will make sure our
62-
// initialization code gets called during startup.
63-
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
64-
private static void Initialize()
65-
{
66-
// This registers the composite with the input system. After calling this
67-
// method, we can have bindings reference the composite. Also, the
68-
// composite will show up in the action editor.
69-
//
70-
// NOTE: We don't supply a name for the composite here. The default logic
71-
// will take the name of the type ("CustomComposite" in our case)
72-
// and snip off "Composite" if used as a suffix (which is the case
73-
// for us) and then use that as the name. So in our case, we are
74-
// registering a composite called "Custom" here.
75-
//
76-
// If we were to use our composite with the AddCompositeBinding API,
77-
// for example, it would look like this:
78-
//
79-
// myAction.AddCompositeBinding("Custom")
80-
// .With("Stick", "<Gamepad>/leftStick")
81-
// .With("Multiplier", "<Gamepad>/rightTrigger");
82-
InputSystem.RegisterBindingComposite<CustomComposite>();
83-
}
84-
8553
// So, we need two parts for our composite. The part that delivers the stick
8654
// value and the part that delivers the axis multiplier. Note that each part
8755
// may be bound to multiple controls. The input system handles that for us

Packages/com.unity.inputsystem/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ however, it has to be formatted properly to pass verification tests.
2222
- Use `ProfilerMarker` instead of `Profiler.BeginSample` and `Profiler.EndSample` when appropriate to enable recording of profiling data.
2323

2424
### Added
25-
- Added tests for Input Action Editor UI for managing action maps (List, create, rename, delete) (ISX-2087)
25+
- Added tests for Input Action Editor UI for managing action maps (List, create, rename, delete) (ISX-2087)
26+
- Added automatic loading of custom extensions of InputProcessor, InputInteraction and InputBindingComposite [ISXB-856]](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-856).
2627

2728
## [1.10.0] - 2024-07-24
2829

Packages/com.unity.inputsystem/InputSystem/Actions/IInputInteraction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ namespace UnityEngine.InputSystem
8080
/// </code>
8181
/// </example>
8282
///
83-
/// Custom interactions can be registered using <see cref="InputSystem.RegisterInteraction"/>. This can be
83+
/// Custom interactions are automatically registered by reflection but it can also be manually registered using <see cref="InputSystem.RegisterInteraction"/>. This can be
8484
/// done at any point during or after startup but has to be done before actions that reference the interaction
8585
/// are enabled or have their controls queried. A good point is usually to do it during loading like so:
8686
///

Packages/com.unity.inputsystem/InputSystem/Actions/InputBindingComposite.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,8 +279,8 @@ internal static string GetDisplayFormatString(string composite)
279279
///
280280
/// The set of composites available in the system is extensible. While some composites are
281281
/// such as <see cref="Composites.Vector2Composite"/> and <see cref="Composites.ButtonWithOneModifier"/>
282-
/// are available out of the box, new composites can be implemented by anyone and simply be
283-
/// registered with <see cref="InputSystem.RegisterBindingComposite{T}"/>.
282+
/// are available out of the box, new composites can be implemented by anyone and simply be autodiscover
283+
/// or manually registered with <see cref="InputSystem.RegisterBindingComposite{T}"/>.
284284
///
285285
/// See the "Custom Composite" sample (can be installed from package manager UI) for a detailed example
286286
/// of how to create a custom composite.

Packages/com.unity.inputsystem/InputSystem/Controls/InputProcessor.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,6 @@ public enum CachingPolicy
117117
///
118118
/// <example>
119119
/// <code>
120-
/// // To register the processor, call
121-
/// //
122-
/// // InputSystem.RegisterProcessor&lt;ScalingProcessor&gt;("scale");
123-
/// //
124120
/// public class ScalingProcessor : InputProcessor&lt;float&gt;
125121
/// {
126122
/// // This field can be set as a parameter. See examples below.

Packages/com.unity.inputsystem/InputSystem/InputManager.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Diagnostics;
44
using System.Linq;
5+
using System.Reflection;
56
using System.Runtime.CompilerServices;
67
using System.Text;
78
using Unity.Collections;
@@ -68,6 +69,7 @@ internal partial class InputManager
6869
static readonly ProfilerMarker k_InputTryFindMatchingControllerMarker = new ProfilerMarker("InputSystem.TryFindMatchingControlLayout");
6970
static readonly ProfilerMarker k_InputAddDeviceMarker = new ProfilerMarker("InputSystem.AddDevice");
7071
static readonly ProfilerMarker k_InputRestoreDevicesAfterReloadMarker = new ProfilerMarker("InputManager.RestoreDevicesAfterDomainReload");
72+
static readonly ProfilerMarker k_InputRegisterCustomTypesMarker = new ProfilerMarker("InputManager.RegisterCustomTypes");
7173

7274
public InputMetrics metrics
7375
{
@@ -1965,6 +1967,65 @@ internal void InitializeData()
19651967
composites.AddTypeRegistration("ButtonWithTwoModifiers", typeof(ButtonWithTwoModifiers));
19661968
composites.AddTypeRegistration("OneModifier", typeof(OneModifierComposite));
19671969
composites.AddTypeRegistration("TwoModifiers", typeof(TwoModifiersComposite));
1970+
1971+
// Register custom types by reflection
1972+
RegisterCustomTypes();
1973+
}
1974+
1975+
void RegisterCustomTypes(Type[] types)
1976+
{
1977+
foreach (Type type in types)
1978+
{
1979+
if (!type.IsClass
1980+
|| type.IsAbstract
1981+
|| type.IsGenericType)
1982+
continue;
1983+
if (typeof(InputProcessor).IsAssignableFrom(type))
1984+
{
1985+
InputSystem.RegisterProcessor(type);
1986+
}
1987+
else if (typeof(IInputInteraction).IsAssignableFrom(type))
1988+
{
1989+
InputSystem.RegisterInteraction(type);
1990+
}
1991+
else if (typeof(InputBindingComposite).IsAssignableFrom(type))
1992+
{
1993+
InputSystem.RegisterBindingComposite(type, null);
1994+
}
1995+
}
1996+
}
1997+
1998+
void RegisterCustomTypes()
1999+
{
2000+
k_InputRegisterCustomTypesMarker.Begin();
2001+
2002+
var inputSystemAssembly = typeof(InputProcessor).Assembly;
2003+
var inputSystemName = inputSystemAssembly.GetName().Name;
2004+
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
2005+
foreach (var assembly in assemblies)
2006+
{
2007+
try
2008+
{
2009+
// exclude InputSystem assembly which should be loaded first
2010+
if (assembly == inputSystemAssembly) continue;
2011+
2012+
// Only register types from assemblies that reference InputSystem
2013+
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
2014+
{
2015+
if (referencedAssembly.Name == inputSystemName)
2016+
{
2017+
RegisterCustomTypes(assembly.GetTypes());
2018+
break;
2019+
}
2020+
}
2021+
}
2022+
catch (ReflectionTypeLoadException)
2023+
{
2024+
continue;
2025+
}
2026+
}
2027+
2028+
k_InputRegisterCustomTypesMarker.End();
19682029
}
19692030

19702031
internal void InstallRuntime(IInputRuntime runtime)

0 commit comments

Comments
 (0)