Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Resolved Implementing pinching

Discussion in 'Input System' started by Tony_Max, Dec 5, 2022.

  1. Tony_Max

    Tony_Max

    Joined:
    Feb 7, 2017
    Posts:
    358
    How I understand Input System actions and bindings
    • Action supposed to be client-action, which is abstraction higher then reading inputs. It is game (application) events like "Jump" or "Shoot".
    • Bindings is just reading inputs in all ways you can achieve it.
    What I want to achieve: implement cross platform camera Zoom action. For mouse device it is simple as adding Mouse/Scroll/Y binding with Clamp(-1, 1) processor, so after that I can "Zoom" anything depending on -1, 0, 1 values.
    With touchscreen device it is much harder. There is some tutorials from devs where they create actions like "Touch", "Drag", etc. But as I've mentioned in 1st section I don't want to use actions in that way. So I've started to search a way to create suitable binding.

    So I've found two possibilities
    • Implement custom processor which will triggered by any Touch #0/Delta or Touch #0/Position (no matter what indeed, because we just want to trigger processor here). Then in
      Process
      method access to actual controls through
      Touchscreen.current.TryGetChildControl<TouchControl>("/touch0");
      (same for /touch1) and then read current position / start position / touch phase. It is working solution but it looks kinda hacky.
    • Implement custom composite binding. It looks like more straight way to implement such things, because in examples when we want to read values from mouse but only when left button is holding we can use composite with one modifier and set modifier to something like <Mouse>/leftButton. But while implementing composite for pinching I see no proper way to make conditional input (source code for OneModifierComposite.cs consist of some under the hood magic which I don't understand so I don't know how it becomes conditional)
    How can it be implemented with composites? How to check if particular composite part is performed by now?

    What I've tried:
    • Check by
      InputBindingCompositeContext.EvaluateMagnitude(partIndex)
      and
      InputBindingCompositeContext.GetPressTime(partIndex)
      but those values never become default after first actuation
    • Check by
      InputBindingCompositeContext.ReadAsButton(partIndex)
      but I get errors
      InvalidOperationException: Cannot read value of type 'float' from control '/Touchscreen/touch0/position' bound to action
    UPD: I've solved it by using /touch #0 and /touch #1 +
    InputBindingCompositeContext.ReadValue<TouchPhase, TouchPhaseComparer>(partIndex)
    then I can read touch phase / position / start position / etc from solid touch data
    Note: while implementing I was getting invalid TouchPhase values without any visible reason. All because of returning 0 in TouchPhaseComparer as compare result, it seems inner code returns some default values when bindings are equal, so returning just 1 instead of 0 solved all problems.
    Code (CSharp):
    1. #if UNITY_EDITOR
    2. using System.Collections.Generic;
    3. using Unity.Mathematics;
    4. using UnityEditor;
    5. using UnityEngine;
    6. using UnityEngine.InputSystem;
    7. using UnityEngine.InputSystem.Layouts;
    8. using UnityEngine.InputSystem.LowLevel;
    9. using UnityEngine.InputSystem.Utilities;
    10. using TouchPhase = UnityEngine.InputSystem.TouchPhase;
    11.  
    12. [InitializeOnLoad]
    13. #endif
    14. [DisplayStringFormat("{firstTouch}+{secondTouch}")]
    15. public class PinchingComposite : InputBindingComposite<float>
    16. {
    17.     [InputControl(layout = "Value")]
    18.     public int firstTouch;
    19.     [InputControl(layout = "Value")]
    20.     public int secondTouch;
    21.  
    22.     public float negativeScale = 1f;
    23.     public float positiveScale = 1f;
    24.  
    25.     private struct TouchStateComparer : IComparer<TouchState>
    26.     {
    27.         public int Compare(TouchState x, TouchState y) => 1;
    28.     }
    29.  
    30.     // This method computes the resulting input value of the composite based
    31.     // on the input from its part bindings.
    32.     public override float ReadValue(ref InputBindingCompositeContext context)
    33.     {
    34.         var touch_0 = context.ReadValue<TouchState, TouchStateComparer>(firstTouch);
    35.         var touch_1 = context.ReadValue<TouchState, TouchStateComparer>(secondTouch);
    36.  
    37.         if (touch_0.phase != TouchPhase.Moved || touch_1.phase != TouchPhase.Moved)
    38.             return 0f;
    39.  
    40.         var startDistance = math.distance(touch_0.startPosition, touch_1.startPosition);
    41.         var distance = math.distance(touch_0.position, touch_1.position);
    42.  
    43.         var unscaledValue = startDistance / distance - 1f; // startDistance divide by distance to invert value
    44.         return unscaledValue * (unscaledValue < 0 ? negativeScale : positiveScale);
    45.     }
    46.  
    47.     // This method computes the current actuation of the binding as a whole.
    48.     public override float EvaluateMagnitude(ref InputBindingCompositeContext context) => 1f;
    49.  
    50.     static PinchingComposite() => InputSystem.RegisterBindingComposite<PinchingComposite>();
    51.  
    52.     [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    53.     static void Init() { } // Trigger static constructor.
    54. }
    upload_2022-12-5_15-30-3.png
     
    Last edited: Dec 5, 2022
    Eulenmensch likes this.
  2. Eulenmensch

    Eulenmensch

    Joined:
    Mar 10, 2018
    Posts:
    19
    Hi Tony,

    Sorry to necro this thread when it was marked as resolved but I'm trying use your composite for a project of mine and am running into some issues. namely, touch_0.phase and touch_1.phase always report as none although I am very much using multiple fingers on my phone. Have you run into this issue while getting your composite to work?

    I'm not asking you to troubleshoot my issue so if theres nothing off the top of your head that you could point to, I'm more than happy to resolve this myself.

    Also thanks for the code you provided and updating a thread purely for people that might stumble across it on their search!
     
    Tony_Max likes this.
  3. Tony_Max

    Tony_Max

    Joined:
    Feb 7, 2017
    Posts:
    358
    I've checked project where I had implemented this composite and it looks like code completely same as I provide here. You can check it here. Also you can download project and try to catch the problem.
     
  4. Eulenmensch

    Eulenmensch

    Joined:
    Mar 10, 2018
    Posts:
    19