OnScreenControl.SendValueToControl does not trigger Action [2019.4.3f1]

I have created a simple clone of OnScreenStick to customize its behavior to suit my purposes. I am finding that calling SendValueToControl does not trigger the corresponding Action setup in my PlayerInput.

I have set the control path of the joystick to Gamepad/LeftStick:

I am testing this on my PC with a keyboard&mouse. I do not have a gamepad connected. Here is the Move action which I expect to fire:

I am using the “Unity Events” configuration of PlayerInput on my Player object to handle the Move action. I can confirm that pressing WASD properly fires the action, but calling SendValueToControl from the OnScreenStick does not. Here is my OnScreenJoystick code, which is nearly identical to the OnScreenStick code:

using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.InputSystem.Layouts;
using UnityEngine;
using UnityEngine.InputSystem.OnScreen;
using UnityEngine.InputSystem;

public class OnScreenJoystick : OnScreenControl, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
    private float m_MovementRange;

    public float movementRange
    {
        get => m_MovementRange;
    }

    [InputControl(layout = "Vector2")]
    [SerializeField]
    private string m_ControlPath;

    [SerializeField]
    private GameObject joystickBoundsImageTemplate;
    private GameObject joystickBoundsImage;

    [SerializeField]
    private GameObject joystickCenterImageTemplate;
    private GameObject joystickCenterImage;

    private Vector2 m_PointerDownPos;

    protected override string controlPathInternal
    {
        get => m_ControlPath;
        set => m_ControlPath = value;
    }

    private void OnEnable()
    {
        if (joystickBoundsImageTemplate == null)
            throw new System.ArgumentNullException(nameof(joystickBoundsImageTemplate));

        if (joystickCenterImageTemplate == null)
            throw new System.ArgumentNullException(nameof(joystickCenterImageTemplate));

        FillScreen();
    }

    public void FillScreen()
    {
        Rect parentRect = (transform.parent as RectTransform).rect;
        ((RectTransform)transform).SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, parentRect.width);
        ((RectTransform)transform).SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, parentRect.height);
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        if (eventData == null)
            throw new System.ArgumentNullException(nameof(eventData));

        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            transform.parent.GetComponentInParent<RectTransform>(),
            eventData.position,
            eventData.pressEventCamera,
            out m_PointerDownPos
        );

        Debug.Log($"Pointer down at {m_PointerDownPos}");

        if (joystickBoundsImage == null)
            joystickBoundsImage = Instantiate(joystickBoundsImageTemplate, transform);
        ((RectTransform)joystickBoundsImage.transform).anchoredPosition = m_PointerDownPos;
        joystickBoundsImage.SetActive(true);

        if (joystickCenterImage == null)
            joystickCenterImage = Instantiate(joystickCenterImageTemplate, transform);
        ((RectTransform)joystickCenterImage.transform).anchoredPosition = m_PointerDownPos;
        joystickCenterImage.SetActive(true);

        Rect bounds = ((RectTransform)joystickBoundsImage.transform).rect;
        m_MovementRange = Mathf.Min(bounds.width, bounds.height) / 2;
    }

    public void OnDrag(PointerEventData eventData)
    {
        if (eventData == null)
            throw new System.ArgumentNullException(nameof(eventData));

        Debug.Log($"OnDrag");

        RectTransformUtility.ScreenPointToLocalPointInRectangle(
            transform.parent.GetComponentInParent<RectTransform>(),
            eventData.position,
            eventData.pressEventCamera,
            out var position
        );

        Debug.Log($"Dragged to {position}");
        var delta = position - m_PointerDownPos;

        delta = Vector2.ClampMagnitude(delta, movementRange);
        ((RectTransform)joystickCenterImage.transform).anchoredPosition = m_PointerDownPos + delta;

        var newPos = new Vector2(delta.x / movementRange, delta.y / movementRange);
        Debug.Log($"Sending position to control: {newPos}");
        SendValueToControl(newPos);
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        if (joystickCenterImage != null)
            joystickCenterImage.SetActive(false);
       
        if (joystickCenterImage != null)
            joystickBoundsImage.SetActive(false);

        SendValueToControl(Vector2.zero);
        Debug.Log($"Pointer up at {m_PointerDownPos}");
    }

}

In the Input Debugger, I do not see a gamepad device, which leads me to believe perhaps the OnScreenControl is not correctly creating a virtual gamepad device:

Here’s the PlayerInput configuration for reference:

From what I can tell, OnScreenStick is broken. I have been wrestling with the new Input System for days. Is there something I’m doing wrong?

Did you ever get an answer to this? I am having the same issue. When using the On-Screen Stick component, setting the “Control Path” to “Left Stick [Gamepad]” and having a “Left Stick [Gamepad]” defined as one of the bindings for my “Move” action would logically mean that this would trigger a CallbackContext, thus allowing me to utilize it to invoke any Unity events I have defined under my “Move” CallbackContext section of my Player Input component. However, no such luck. The Left Stick inputs are definitely being sent to the input system (because I am using them to read their Vector2 components via a non-CallbackContext method). However, they are definitely not being recognized through the CallbackContext system via SendValueToControl.

I don’t believe that the issue resides with the On-Screen Stick component. I hooked up a controller and it will not trigger the CallbackContext routine regardless of pairing it with the On-Screen Stick component. From what I can tell, the only thing I can get to work with CallbackContext are direct key presses on a keyboard without incorporating SendValueToControl.

Here is my solution :