Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Errors are thrown when assigning Input Actions Asset that contains a custom InputProcessor or InputBindingComposite as Project-wide Input Actions (ISXB-856) #1958

Merged
merged 21 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 13 additions & 45 deletions Assets/Samples/CustomComposite/CustomComposite.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,61 +27,29 @@
// our composite will not be shown as our value type (Vector2) is
// incompatible with the value type of Axis (float).
//
// Also, we need to register our composite with the input system. And we
// want to do it in a way that makes the composite visible in the action
// editor of the input system.
//
// For that to happen, we need to call InputSystem.RegisterBindingComposite
// sometime during startup. We make that happen by using [InitializeOnLoad]
// in the editor and [RuntimeInitializeOnLoadMethod] in the player.
#if UNITY_EDITOR
[InitializeOnLoad]
#endif
// We can customize the way display strings are formed for our composite by
// annotating it with DisplayStringFormatAttribute. The string is simply a
// list with elements to be replaced enclosed in curly braces. Everything
// outside those will taken verbatim. The fragments inside the curly braces
// in this case refer to the binding composite parts by name. Each such
// instance is replaced with the display text for the corresponding
// part binding.
//
// NOTE: We don't supply a name for the composite here. The default logic
// will take the name of the type ("CustomComposite" in our case)
// and snip off "Composite" if used as a suffix (which is the case
// for us) and then use that as the name. So in our case, we are
// registering a composite called "Custom" here.
//
// If we were to use our composite with the AddCompositeBinding API,
// for example, it would look like this:
//
// myAction.AddCompositeBinding("Custom")
// .With("Stick", "<Gamepad>/leftStick")
// .With("Multiplier", "<Gamepad>/rightTrigger");
[DisplayStringFormat("{multiplier}*{stick}")]
public class CustomComposite : InputBindingComposite<Vector2>
{
// In the editor, the static class constructor will be called on startup
// because of [InitializeOnLoad].
#if UNITY_EDITOR
static CustomComposite()
{
// Trigger our RegisterBindingComposite code in the editor.
Initialize();
}

#endif

// In the player, [RuntimeInitializeOnLoadMethod] will make sure our
// initialization code gets called during startup.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Initialize()
{
// This registers the composite with the input system. After calling this
// method, we can have bindings reference the composite. Also, the
// composite will show up in the action editor.
//
// NOTE: We don't supply a name for the composite here. The default logic
// will take the name of the type ("CustomComposite" in our case)
// and snip off "Composite" if used as a suffix (which is the case
// for us) and then use that as the name. So in our case, we are
// registering a composite called "Custom" here.
//
// If we were to use our composite with the AddCompositeBinding API,
// for example, it would look like this:
//
// myAction.AddCompositeBinding("Custom")
// .With("Stick", "<Gamepad>/leftStick")
// .With("Multiplier", "<Gamepad>/rightTrigger");
InputSystem.RegisterBindingComposite<CustomComposite>();
}

// So, we need two parts for our composite. The part that delivers the stick
// value and the part that delivers the axis multiplier. Note that each part
// may be bound to multiple controls. The input system handles that for us
Expand Down
1 change: 1 addition & 0 deletions Packages/com.unity.inputsystem/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ however, it has to be formatted properly to pass verification tests.

### Added
- Added `InputSystemUIInputModule.scrollDeltaPerTick` property, a customizable multiplicative factor applied to the scroll wheel speed before it is sent to UI components. Note that this has no effect on UI Toolkit content, only uGUI.
- Added automatic loading of custom extensions of InputProcessor, InputInteraction and InputBindingComposite [ISXB-856]](https://issuetracker.unity3d.com/product/unity/issues/guid/ISXB-856).

## [1.9.0] - 2024-07-15

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ namespace UnityEngine.InputSystem
/// </code>
/// </example>
///
/// Custom interactions can be registered using <see cref="InputSystem.RegisterInteraction"/>. This can be
/// Custom interactions are automatically registed by reflexion but it can also be manually registered using <see cref="InputSystem.RegisterInteraction"/>. This can be
bmalrat marked this conversation as resolved.
Show resolved Hide resolved
/// done at any point during or after startup but has to be done before actions that reference the interaction
/// are enabled or have their controls queried. A good point is usually to do it during loading like so:
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@ internal static string GetDisplayFormatString(string composite)
///
/// The set of composites available in the system is extensible. While some composites are
/// such as <see cref="Composites.Vector2Composite"/> and <see cref="Composites.ButtonWithOneModifier"/>
/// are available out of the box, new composites can be implemented by anyone and simply be
/// registered with <see cref="InputSystem.RegisterBindingComposite{T}"/>.
/// are available out of the box, new composites can be implemented by anyone and simply be autodiscover
/// or manually registered with <see cref="InputSystem.RegisterBindingComposite{T}"/>.
///
/// See the "Custom Composite" sample (can be installed from package manager UI) for a detailed example
/// of how to create a custom composite.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,6 @@ public enum CachingPolicy
///
/// <example>
/// <code>
/// // To register the processor, call
/// //
/// // InputSystem.RegisterProcessor&lt;ScalingProcessor&gt;("scale");
/// //
/// public class ScalingProcessor : InputProcessor&lt;float&gt;
/// {
/// // This field can be set as a parameter. See examples below.
Expand Down
56 changes: 56 additions & 0 deletions Packages/com.unity.inputsystem/InputSystem/InputManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using Unity.Collections;
Expand Down Expand Up @@ -1965,6 +1966,61 @@ internal void InitializeData()
composites.AddTypeRegistration("ButtonWithTwoModifiers", typeof(ButtonWithTwoModifiers));
composites.AddTypeRegistration("OneModifier", typeof(OneModifierComposite));
composites.AddTypeRegistration("TwoModifiers", typeof(TwoModifiersComposite));

// Register custom types by reflection
RegisterCustomTypes();
}

void RegisterCustomTypes(Type[] types)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make sure to add a ProfilerMarker to track how long this takes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea I will add that

Copy link
Collaborator

@jfreire-unity jfreire-unity Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, that sounds good. @chris-massie are you concerned with startup times with this change?

{
foreach (Type type in types)
{
if (!type.IsClass
|| type.IsAbstract
|| type.IsGenericType)
continue;
if (typeof(InputProcessor).IsAssignableFrom(type))
{
InputSystem.RegisterProcessor(type);
}
else if (typeof(IInputInteraction).IsAssignableFrom(type))
{
InputSystem.RegisterInteraction(type);
}
else if (typeof(InputBindingComposite).IsAssignableFrom(type))
{
InputSystem.RegisterBindingComposite(type, null);
}
}
}

void RegisterCustomTypes()
{
var inputSystemAssembly = typeof(InputProcessor).Assembly;
var inputSystemName = inputSystemAssembly.GetName().Name;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
// exclude InputSystem assembly which should be loaded first
if (assembly == inputSystemAssembly) continue;

// Only register types from assemblies that reference InputSystem
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
{
if (referencedAssembly.Name == inputSystemName)
{
RegisterCustomTypes(assembly.GetTypes());
break;
}
}
}
catch (ReflectionTypeLoadException)
{
continue;
}
}
}

internal void InstallRuntime(IInputRuntime runtime)
Expand Down