Search Unity

Vector3 Composite

Discussion in 'Input System' started by DevinW, Oct 24, 2019.

  1. DevinW

    DevinW

    Joined:
    Jun 19, 2014
    Posts:
    37
    Is there any way to create my own composite? I wrote a Vector3 composite that was based on the Vector2 one but the GUI doesn't seem to register it.
     
  2. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Make sure to have the RegisterBindingComposite call in place. In the editor, needs to happen as part of code that runs after domain reloads (e.g. InitializeOnLoad). See the [InitializeOnLoad] setup in the docs.

    Also, note that composites get filtered based on value type. The system will automatically try to find the InputBindingComposite<TValue> base class and extract the TValue type. If this base class is present and the TValue type is not compatible with the value type of the expected "Control Type" set on the action, the composite isn't shown.
     
  3. Jichaels

    Jichaels

    Joined:
    Dec 27, 2018
    Posts:
    237
    Just curious, why would you want a vector 3 for input ? Which device ?
     
  4. DevinW

    DevinW

    Joined:
    Jun 19, 2014
    Posts:
    37
    Basically the same reason you'd want a Vector2 composite.

    In some cases, a character would move with W A S D but also up and down with Q and E (see City of Heroes flying)

    In my case editor/pc controls to move a vr rig around mostly for debugging.


    Thanks Rene-Damm, that documentation is just what I needed. I think this is something that should probably just be there by default. full 3D movement is pretty common in XR and with not other obvious vector3 inputs other than tracked devices it's a bit frustrating I had to go down this route. Thankfully it was fairly simple in the end.
     
    Last edited: Oct 27, 2019
    Rallix likes this.
  5. Rene-Damm

    Rene-Damm

    Joined:
    Sep 15, 2012
    Posts:
    1,779
    Yup makes sense. Added it to the list of things to look at after 1.0.
     
    MUGIK likes this.
  6. DevinW

    DevinW

    Joined:
    Jun 19, 2014
    Posts:
    37
    I just did the XRI demo and was sad to see new input system didn't make it into that.

    Hopefully soon.

    And also hope the Vector3 composite it still added after or before the input system's release.

    Good luck to everyone on the new input team <3
     
  7. Steven_VR

    Steven_VR

    Joined:
    Jan 11, 2020
    Posts:
    8
    What was the result on this topic?
    I have a use case for what is described above (Q and E for camera up and down) but I can't seem to decipher the input system to determine the best solution to assign the keys to that movement. I'm not a super savvy Unity coder so trying to piece this together watching tutorials but all the tutorials are happy with the magic of WASD working and stop there. :)
     
  8. Laocoon7

    Laocoon7

    Joined:
    Jun 8, 2019
    Posts:
    5
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEditor;
    4. using UnityEngine.InputSystem;
    5. using UnityEngine.InputSystem.Editor;
    6. using UnityEngine.InputSystem.Utilities;
    7. using UnityEngine.InputSystem.Layouts;
    8.  
    9. #if UNITY_EDITOR
    10. public class CustomParameterEditor : InputParameterEditor<Custom3DVectorComposite>
    11. {
    12.     public override void OnGUI()
    13.     {
    14.         //EditorGUILayout.LabelField("3D Vector Composite");
    15.         //target.floatParameter = EditorGUILayout.FloatField("Some Parameter", target.floatParameter);
    16.         target.modeParameter = (Custom3DVectorComposite.Mode) EditorGUILayout.EnumPopup("Mode", target.modeParameter);
    17.     }
    18. }
    19. #endif
    20.  
    21.  
    22. // Use InputBindingComposite<TValue> as a base class for a composite that returns
    23. // values of type TValue.
    24. // NOTE: It is possible to define a composite that returns different kinds of values
    25. //       but doing so requires deriving directly from InputBindingComposite.
    26. #if UNITY_EDITOR
    27. [InitializeOnLoad] // Automatically register in editor.
    28. #endif
    29. // Determine how GetBindingDisplayString() formats the composite by applying
    30. // the  DisplayStringFormat attribute.
    31. [DisplayStringFormat("{firstPart}+{secondPart}")]
    32. public class Custom3DVectorComposite : InputBindingComposite<Vector3>
    33. {
    34.     // Each part binding is represented as a field of type int and annotated with
    35.     // InputControlAttribute. Setting "layout" restricts the controls that
    36.     // are made available for picking in the UI.
    37.     //
    38.     // On creation, the int value is set to an integer identifier for the binding
    39.     // part. This identifier can read values from InputBindingCompositeContext.
    40.     // See ReadValue() below.
    41.     [InputControl(layout = "Value")]
    42.     public int left;
    43.  
    44.     [InputControl(layout = "Value")]
    45.     public int right;
    46.  
    47.     [InputControl(layout = "Value")]
    48.     public int down;
    49.  
    50.     [InputControl(layout = "Value")]
    51.     public int up;
    52.  
    53.     [InputControl(layout = "Value")]
    54.     public int backward;
    55.  
    56.     [InputControl(layout = "Value")]
    57.     public int forward;
    58.  
    59.  
    60.     // Any public field that is not annotated with InputControlAttribute is considered
    61.     // a parameter of the composite. This can be set graphically in the UI and also
    62.     // in the data (e.g. "custom(floatParameter=2.0)").
    63.  
    64.     public enum Mode
    65.     {
    66.         Analog = 0,             // As-is
    67.         DigitalNormalized = 1,  // Normalized
    68.         Digital = 2             // Non-Normalized 1, 0, -1 values
    69.     }
    70.     public Mode modeParameter = Mode.DigitalNormalized;
    71.     //public float floatParameter;
    72.     //public bool boolParameter;
    73.  
    74.     // This method computes the resulting input value of the composite based
    75.     // on the input from its part bindings.
    76.     public override Vector3 ReadValue(ref InputBindingCompositeContext context)
    77.     {
    78.         var leftValue = context.ReadValue<float>(left);
    79.         var rightValue = context.ReadValue<float>(right);
    80.         var downValue = context.ReadValue<float>(down);
    81.         var upValue = context.ReadValue<float>(up);
    82.         var backwardValue = context.ReadValue<float>(backward);
    83.         var forwardValue = context.ReadValue<float>(forward);
    84.  
    85.         Vector3 returnVector = new Vector3(rightValue - leftValue, upValue - downValue, forwardValue - backwardValue);
    86.  
    87.         switch (modeParameter)
    88.         {
    89.             case Mode.Analog:
    90.                 break;
    91.             case Mode.DigitalNormalized:
    92.                 returnVector.Normalize();
    93.                 break;
    94.             case Mode.Digital:
    95.                 returnVector = MakeDigital(returnVector);
    96.                 break;
    97.             default:
    98.                 Debug.LogError($"Custom3DVectorComposite.EvaluateMagnitude: Unrecognized Mode[{modeParameter}]");
    99.                 throw new ArgumentOutOfRangeException($"Custom3DVectorComposite.EvaluateMagnitude: Unrecognized Mode[{modeParameter}]");
    100.         }
    101.  
    102.         //... do some processing and return value
    103.         return returnVector;
    104.     }
    105.  
    106.     // This method computes the current actuation of the binding as a whole.
    107.     public override float EvaluateMagnitude(ref InputBindingCompositeContext context)
    108.     {
    109.         // Compute normalized [0..1] magnitude value for current actuation level.
    110.        
    111.         return -1;
    112.     }
    113.  
    114.     Vector3 MakeDigital(Vector3 vector)
    115.     {
    116.         var returnVector = Vector3.zero;
    117.         if (vector.x > 0)
    118.             returnVector.x = 1;
    119.         else if (vector.x < 0)
    120.             returnVector.x = -1;
    121.         else
    122.             returnVector.x = 0;
    123.  
    124.         if (vector.y > 0)
    125.             returnVector.y = 1;
    126.         else if (vector.y < 0)
    127.             returnVector.y = -1;
    128.         else
    129.             returnVector.y = 0;
    130.  
    131.         if (vector.z > 0)
    132.             returnVector.z = 1;
    133.         else if (vector.z < 0)
    134.             returnVector.z = -1;
    135.         else
    136.             returnVector.z = 0;
    137.  
    138.         return returnVector;
    139.     }
    140.  
    141.     static Custom3DVectorComposite()
    142.     {
    143.         // Can give custom name or use default (type name with "Composite" clipped off).
    144.         // Same composite can be registered multiple times with different names to introduce
    145.         // aliases.
    146.         //
    147.         // NOTE: Registering from the static constructor using InitializeOnLoad and
    148.         //       RuntimeInitializeOnLoadMethod is only one way. You can register the
    149.         //       composite from wherever it works best for you. Note, however, that
    150.         //       the registration has to take place before the composite is first used
    151.         //       in a binding. Also, for the composite to show in the editor, it has
    152.         //       to be registered from code that runs in edit mode.
    153.         InputSystem.RegisterBindingComposite<Custom3DVectorComposite>();
    154.     }
    155.  
    156.     [RuntimeInitializeOnLoadMethod]
    157.     static void Init() { } // Trigger static constructor.
    158. }
    159.  
    An "ugly" implementation thrown together off the https://docs.unity3d.com/Packages/c...95368388.1581841810#writing-custom-composites link posted above.

    Howto:
    1. Add this into a script (named Custom3DVectorComposite.cs) and place it in an "editor" folder.

    2. Create a new Action, mark it as Value / Vector3, it'll appear under the Add Binding menu.
     
    Skaltum likes this.
  9. owen_proto

    owen_proto

    Joined:
    Mar 18, 2018
    Posts:
    118
    Thanks @Laocoon7.
    I actually ended up here trying to find more information about what exactly the EvaluateMagnitude function is supposed to be returning. Can anyone explain, perhaps with a 3D Vector composite as an example?
     
  10. Spemble

    Spemble

    Joined:
    Mar 20, 2016
    Posts:
    7
    If you don't want to make a custom extension, here's a solution:

    For 3D movement with WASDQE, you can break it apart into each axis.

    upload_2021-8-12_23-16-42.png

    (To get 1D Axis shown above, make sure to select Control Type = Any, then click the plus with an arrow in the blue area in the picture and click "Add 1D Axis Composite")

    Now combine the axes into one Vector3 in the code. Here's an example:

    Code (CSharp):
    1.  
    2.     public float thrust = 3;
    3.     public float thrustMax = 10;
    4.     public float sprintMult = 2;
    5.  
    6.     Rigidbody rb;
    7.     PlayerControls controls;
    8.     Vector3 inputDir;
    9.     float sprintAmount;
    10.  
    11.     void Awake() {
    12.         rb = GetComponent<Rigidbody>();
    13.         controls = new PlayerControls();
    14.         controls.PlayerMovement.MovementX.performed += ctx => inputDir.x = ctx.ReadValue<float>();
    15.         controls.PlayerMovement.MovementY.performed += ctx => inputDir.y = ctx.ReadValue<float>();
    16.         controls.PlayerMovement.MovementZ.performed += ctx => inputDir.z = ctx.ReadValue<float>();
    17.         controls.PlayerMovement.Sprint.performed += ctx => sprintAmount = ctx.ReadValue<float>();
    18.     }
    19.  
    20.     void FixedUpdate() {
    21.         ProcessMovement();
    22.     }
    23.     void OnEnable() {
    24.         controls.Enable();
    25.     }
    26.     void OnDisable() {
    27.         controls.Disable();
    28.     }
    29.  
    30.     Vector3 thrustDir; // GC buster
    31.     void ProcessMovement() {
    32.         if (inputDir == Vector3.zero) { return; }
    33.         thrustDir = inputDir.normalized;
    34.         if (sprintAmount > 0) { thrustDir *= 1 + (sprintMult - 1) * sprintAmount; }
    35.         rb.AddForce((thrustDir * thrustMax - rb.velocity) * thrust); // Non-traditional and has some flaws
    36.     }
    37.  
    Hope this saves someone some time. ^_^
     
  11. DSivtsov

    DSivtsov

    Joined:
    Feb 20, 2019
    Posts:
    151
    Good, But the inputDir must be set to Vector3.zero "by hand"(after use) in other case it will store the last values.

    In My case the next part of your code never work.
    or add additional code
    Code (CSharp):
    1.  
    2.         [SerializeField] private PlayerInput playerInput;
    3.         InputActionMap _actionsCamMove;
    4.  
    5.         private void Awake()
    6.         {
    7.             _actionsCamMove = playerInput.actions.FindActionMap("CameraControls");
    8.             if (_actionsCamMove != null)
    9.             {
    10.                 Debug.Log($"{_actionsCamMove.asset.name}");
    11.             }
    12.         }
    13.  
    14.         void OnEnable()
    15.         {
    16.             _actionsCamMove.Enable();
    17.             _actionsCamMove.actionTriggered += _actionCamMove_actionTriggered;
    18.  
    19.             _actionsCamMove.FindAction("MovementX").performed += ctx => inputDir.x = ctx.ReadValue<float>();
    20.             _actionsCamMove.FindAction("MovementY").performed += ctx => inputDir.y = ctx.ReadValue<float>();
    21.             _actionsCamMove.FindAction("MovementZ").performed += ctx => inputDir.z = ctx.ReadValue<float>();
    22.  }
    23.  
    24.         private void _actionCamMove_actionTriggered(InputAction.CallbackContext obj)
    25.         {
    26.             if (obj.canceled)
    27.                 inputDir = Vector3.zero; //Debug.Log($"Action: {obj.action} phase: {obj.phase} Value: {obj.ReadValue<float>()}");
    28.  
    29.         }
    P.S>
    But the standard WASD (Composite Value Vector 2) self set to Vector2.zero when "Input action is absent"
    StandardWASD.JPG
     
    Last edited: Aug 24, 2021
  12. DSivtsov

    DSivtsov

    Joined:
    Feb 20, 2019
    Posts:
    151
    FYI
    In the release Input.System.1.1.1 (September 09, 2021) was added three new binding composite types, also include the "Vector3Composite: Works the same way Vector2Composite does. Adds a forward and backward binding in addition to up, down, left, and right."
    InputSystem.1.1.1.JPG