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.
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.
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.
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
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.
Code (CSharp): using System; using UnityEngine; using UnityEditor; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Editor; using UnityEngine.InputSystem.Utilities; using UnityEngine.InputSystem.Layouts; #if UNITY_EDITOR public class CustomParameterEditor : InputParameterEditor<Custom3DVectorComposite> { public override void OnGUI() { //EditorGUILayout.LabelField("3D Vector Composite"); //target.floatParameter = EditorGUILayout.FloatField("Some Parameter", target.floatParameter); target.modeParameter = (Custom3DVectorComposite.Mode) EditorGUILayout.EnumPopup("Mode", target.modeParameter); } } #endif // Use InputBindingComposite<TValue> as a base class for a composite that returns // values of type TValue. // NOTE: It is possible to define a composite that returns different kinds of values // but doing so requires deriving directly from InputBindingComposite. #if UNITY_EDITOR [InitializeOnLoad] // Automatically register in editor. #endif // Determine how GetBindingDisplayString() formats the composite by applying // the DisplayStringFormat attribute. [DisplayStringFormat("{firstPart}+{secondPart}")] public class Custom3DVectorComposite : InputBindingComposite<Vector3> { // Each part binding is represented as a field of type int and annotated with // InputControlAttribute. Setting "layout" restricts the controls that // are made available for picking in the UI. // // On creation, the int value is set to an integer identifier for the binding // part. This identifier can read values from InputBindingCompositeContext. // See ReadValue() below. [InputControl(layout = "Value")] public int left; [InputControl(layout = "Value")] public int right; [InputControl(layout = "Value")] public int down; [InputControl(layout = "Value")] public int up; [InputControl(layout = "Value")] public int backward; [InputControl(layout = "Value")] public int forward; // Any public field that is not annotated with InputControlAttribute is considered // a parameter of the composite. This can be set graphically in the UI and also // in the data (e.g. "custom(floatParameter=2.0)"). public enum Mode { Analog = 0, // As-is DigitalNormalized = 1, // Normalized Digital = 2 // Non-Normalized 1, 0, -1 values } public Mode modeParameter = Mode.DigitalNormalized; //public float floatParameter; //public bool boolParameter; // This method computes the resulting input value of the composite based // on the input from its part bindings. public override Vector3 ReadValue(ref InputBindingCompositeContext context) { var leftValue = context.ReadValue<float>(left); var rightValue = context.ReadValue<float>(right); var downValue = context.ReadValue<float>(down); var upValue = context.ReadValue<float>(up); var backwardValue = context.ReadValue<float>(backward); var forwardValue = context.ReadValue<float>(forward); Vector3 returnVector = new Vector3(rightValue - leftValue, upValue - downValue, forwardValue - backwardValue); switch (modeParameter) { case Mode.Analog: break; case Mode.DigitalNormalized: returnVector.Normalize(); break; case Mode.Digital: returnVector = MakeDigital(returnVector); break; default: Debug.LogError($"Custom3DVectorComposite.EvaluateMagnitude: Unrecognized Mode[{modeParameter}]"); throw new ArgumentOutOfRangeException($"Custom3DVectorComposite.EvaluateMagnitude: Unrecognized Mode[{modeParameter}]"); } //... do some processing and return value return returnVector; } // This method computes the current actuation of the binding as a whole. public override float EvaluateMagnitude(ref InputBindingCompositeContext context) { // Compute normalized [0..1] magnitude value for current actuation level. return -1; } Vector3 MakeDigital(Vector3 vector) { var returnVector = Vector3.zero; if (vector.x > 0) returnVector.x = 1; else if (vector.x < 0) returnVector.x = -1; else returnVector.x = 0; if (vector.y > 0) returnVector.y = 1; else if (vector.y < 0) returnVector.y = -1; else returnVector.y = 0; if (vector.z > 0) returnVector.z = 1; else if (vector.z < 0) returnVector.z = -1; else returnVector.z = 0; return returnVector; } static Custom3DVectorComposite() { // Can give custom name or use default (type name with "Composite" clipped off). // Same composite can be registered multiple times with different names to introduce // aliases. // // NOTE: Registering from the static constructor using InitializeOnLoad and // RuntimeInitializeOnLoadMethod is only one way. You can register the // composite from wherever it works best for you. Note, however, that // the registration has to take place before the composite is first used // in a binding. Also, for the composite to show in the editor, it has // to be registered from code that runs in edit mode. InputSystem.RegisterBindingComposite<Custom3DVectorComposite>(); } [RuntimeInitializeOnLoadMethod] static void Init() { } // Trigger static constructor. } 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.
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?
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. (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): public float thrust = 3; public float thrustMax = 10; public float sprintMult = 2; Rigidbody rb; PlayerControls controls; Vector3 inputDir; float sprintAmount; void Awake() { rb = GetComponent<Rigidbody>(); controls = new PlayerControls(); controls.PlayerMovement.MovementX.performed += ctx => inputDir.x = ctx.ReadValue<float>(); controls.PlayerMovement.MovementY.performed += ctx => inputDir.y = ctx.ReadValue<float>(); controls.PlayerMovement.MovementZ.performed += ctx => inputDir.z = ctx.ReadValue<float>(); controls.PlayerMovement.Sprint.performed += ctx => sprintAmount = ctx.ReadValue<float>(); } void FixedUpdate() { ProcessMovement(); } void OnEnable() { controls.Enable(); } void OnDisable() { controls.Disable(); } Vector3 thrustDir; // GC buster void ProcessMovement() { if (inputDir == Vector3.zero) { return; } thrustDir = inputDir.normalized; if (sprintAmount > 0) { thrustDir *= 1 + (sprintMult - 1) * sprintAmount; } rb.AddForce((thrustDir * thrustMax - rb.velocity) * thrust); // Non-traditional and has some flaws } Hope this saves someone some time. ^_^
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): [SerializeField] private PlayerInput playerInput; InputActionMap _actionsCamMove; private void Awake() { _actionsCamMove = playerInput.actions.FindActionMap("CameraControls"); if (_actionsCamMove != null) { Debug.Log($"{_actionsCamMove.asset.name}"); } } void OnEnable() { _actionsCamMove.Enable(); _actionsCamMove.actionTriggered += _actionCamMove_actionTriggered; _actionsCamMove.FindAction("MovementX").performed += ctx => inputDir.x = ctx.ReadValue<float>(); _actionsCamMove.FindAction("MovementY").performed += ctx => inputDir.y = ctx.ReadValue<float>(); _actionsCamMove.FindAction("MovementZ").performed += ctx => inputDir.z = ctx.ReadValue<float>(); } private void _actionCamMove_actionTriggered(InputAction.CallbackContext obj) { if (obj.canceled) inputDir = Vector3.zero; //Debug.Log($"Action: {obj.action} phase: {obj.phase} Value: {obj.ReadValue<float>()}"); } P.S> But the standard WASD (Composite Value Vector 2) self set to Vector2.zero when "Input action is absent"
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."