I created a custom composite that is meant to allow a user to click and drag to rotate an object. The problem is that the action triggers the performed callback any time that I either click, or move the mouse. Is there a way that I can change the phases of the composite so that it is only performed when the user clicks and moves the mouse?
A composite has no control over the interaction affecting an action. It’s just a value funnel.
In the current setup, requiring both sourcing of input from multiple controls and pattern recognition on top of it means that both a composite and an interaction is required. The interaction consumes the value created by the composite and performs pattern recognition on top of it.
So, I will need to create an interaction that I can add onto the composite binding? Right now, I have ReadValue overridden to return a Vector2 (Mouse delta in my case) if the modifier button is pressed, otherwise it just returns vector2.zero. Can I have an overloaded ReadValue function in my composite that will return whether the button is pressed or not, or is there a better way to get that information into the interaction?
Based off of what you said about the composite acting like a funnel for the value, I will have to have an explicit way of getting the button state. I am just now realizing that it would make sense to just add a separate function in to read just the button state.
This would be doable with the stock OneModifierComposite.
Reading your initial description, it doesn’t sound like you actually need something more than that. Having a Vector2 value action bound through a OneModifierComposite with the modifier being the left mouse button and the other binding being the mouse delta should do what you’re aiming for. Only while LMB is down will the composite produce non-zero values. Thus, the action will start when that is the case and stop as soon as LMB goes up. However, deltas being what they are (they get reset between every frame), there will be a lot more starting and stopping in-between. Easiest way to deal with that is to stay away from callbacks and just poll the action directly.
// Produces a non-zero vector whenever LMB is down
// and the mouse is moved.
if (action.ReadValue<Vector2())
/* .. */;
I tried using the one modifier composite, but I was getting errors saying that it wasn’t able to read a Vector2. I will try the polling option and see if that works.
Note that there’s two similar composites. ButtonWithOneModifierComposite (legacy; can only handle buttons) and OneModifierComposite (newer; works with any value type in the “binding” part; “modifier” part needs to be button). The latter binds fine to mouse deltas and delivers Vector2s.
Interestingly enough, If I read directly from the action, then it will return a zero vector when the modifier is not pressed, but the second I press the modifier, I get errors. The one modifier composite is expecting a float, so it fails to read a Vector2.
Errors:
Exception ‘InvalidOperationException’ thrown from state change monitor ‘InputActionState’ on ‘Button:/Mouse/leftButton’
UnityEngine.InputSystem.LowLevel.NativeInputRuntime/<>c__DisplayClass7_0:<set_onUpdate>b__0 (UnityEngineInternal.Input.NativeInputUpdateType,UnityEngineInternal.Input.NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate (UnityEngineInternal.Input.NativeInputUpdateType,intptr)
InvalidOperationException: Cannot read value of type ‘float’ from control ‘/Mouse/delta’ bound to action ‘CameraControls/Drag[/Mouse/leftButton,/Mouse/delta]’ (control is a ‘Vector2Control’ with value type ‘Vector2’)
UnityEngine.InputSystem.InputActionState.ReadValue[TValue] (System.Int32 bindingIndex, System.Int32 controlIndex, System.Boolean ignoreComposites) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/InputActionState.cs:2038)
UnityEngine.InputSystem.InputActionState.ReadCompositePartValue[TValue,TComparer] (System.Int32 bindingIndex, System.Int32 partNumber, System.Boolean* buttonValuePtr, System.Int32& controlIndex, TComparer comparer) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/InputActionState.cs:2101)
UnityEngine.InputSystem.InputBindingCompositeContext.ReadValue[TValue] (System.Int32 partNumber) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/InputBindingCompositeContext.cs:92)
UnityEngine.InputSystem.Composites.ButtonWithOneModifier.ReadValue (UnityEngine.InputSystem.InputBindingCompositeContext& context) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/Composites/ButtonWithOneModifier.cs:85)
UnityEngine.InputSystem.Composites.ButtonWithOneModifier.EvaluateMagnitude (UnityEngine.InputSystem.InputBindingCompositeContext& context) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/Composites/ButtonWithOneModifier.cs:97)
UnityEngine.InputSystem.InputActionState.ComputeMagnitude (System.Int32 bindingIndex, System.Int32 controlIndex) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/InputActionState.cs:1914)
UnityEngine.InputSystem.InputActionState.IsActuated (UnityEngine.InputSystem.InputActionState+TriggerState& trigger, System.Single threshold) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/InputActionState.cs:1886)
UnityEngine.InputSystem.InputActionState.ProcessDefaultInteraction (UnityEngine.InputSystem.InputActionState+TriggerState& trigger, System.Int32 actionIndex) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/InputActionState.cs:1275)
UnityEngine.InputSystem.InputActionState.ProcessControlStateChange (System.Int32 mapIndex, System.Int32 controlIndex, System.Int32 bindingIndex, System.Double time, UnityEngine.InputSystem.LowLevel.InputEventPtr eventPtr) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/InputActionState.cs:952)
UnityEngine.InputSystem.InputActionState.UnityEngine.InputSystem.LowLevel.IInputStateChangeMonitor.NotifyControlStateChanged (UnityEngine.InputSystem.InputControl control, System.Double time, UnityEngine.InputSystem.LowLevel.InputEventPtr eventPtr, System.Int64 mapControlAndBindingIndex) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/Actions/InputActionState.cs:833)
UnityEngine.InputSystem.InputManager.FireStateChangeNotifications (System.Int32 deviceIndex, System.Double internalTime, UnityEngine.InputSystem.LowLevel.InputEvent* eventPtr) (at Library/PackageCache/com.unity.inputsystem@1.0.2/InputSystem/InputManager.cs:2914)
UnityEngine.InputSystem.LowLevel.<>c__DisplayClass7_0:<set_onUpdate>b__0(NativeInputUpdateType, NativeInputEventBuffer*)
UnityEngineInternal.Input.NativeInputSystem:NotifyUpdate(NativeInputUpdateType, IntPtr)
I see. Can I create that from the Input action editor window, or will I need to do it in code?
These are the options I have when the action type is value, and control type is any.
That’s the wrong composite. Can indeed only read floats.
Which version of the input package are you on? OneModifierComposite was introduced sometime in or after 1.1. The latest 1.3 definitely has it.
Looks like Unity decided to use 1.0.2 despite there being newer versions. Thanks for the help.
Yeah, it’s very fond of that version for some reason.