Capture buttons pressed on video game control reliably

Asked

Viewed 862 times

8

I want to use an Xbox controller to control a program. It’s not a game, it’s just an app Windows Forms. I don’t want to use XNA because I don’t want to force the redistributables to download when using my application on another machine.

I’m doing some experiments dealing with Directx, using some libraries that encapsulate it. From the documentation, it seems to me that there is no button-pressing event.

So I’m looking for an alternative. I tried to use a timer (with the class System.Windows.Forms.Timer), in whose event I read the state of control in this way:

private void timer_tick(object sender, EventArgs e)
{
    State s = controller.GetState();
    stateLabel.Text = s.Gamepad.Buttons == GamepadButtonFlags.A ? "A" : "";
}

Problem: Between two executions of this method, I have no way of knowing if the user has pressed the button twice, or if he has been holding the button all the time. Even using the information of the package number sent by the control does not help, because a package is sent to the least contact with the analog directional or triggers on the shoulders of the control.

Does anyone know any more reliable way to do that?

  • 1

    Are you facing problems in practice, or is it just a possibility that occurred to you? Everything I’ve read on the subject in one way or another involves Polling as you are doing, so unfortunately I have nothing better to suggest... but perhaps someone with more experience has a better alternative.

  • 3

    It’s a possibility for now, but it will be practical soon. I’m trying to adapt controls so that paraplegic people can use them. Not to play, but to operate a computer.

  • 1

    Perhaps it was the case that you rethink your usability requirements then. After all, your current method will only bring problems if there is the possibility of the person pressing the button several times in sequence faster than your timer can follow. If there is no motive for her to do so (i.e. if there is no use case in which this action produces a desirable result) then the need to distinguish between the two cases becomes less important. Not that it answers the question - I think it’s still valid - but in practice it might be useful.

1 answer

5


Instead of implementing this in a timer, why not use another thread, and continuously check the state of the controller by triggering an event in the form when a button is pressed and when it is released.

Form Code:

public partial class FormWithExternalEvents : Form
{
    private GamepadEvents gpe;

    public FormWithExternalEvents()
    {
        InitializeComponent();
    }

    protected override void OnLoad(EventArgs e)
    {
        gpe = new GamepadEvents();
        gpe.JoystickButtonDown += b =>
        {
            if (this.InvokeRequired) this.Invoke((GamepadButtonEvent) gpe_JoystickButtonDown);
            else gpe_JoystickButtonDown(b);
        };
        gpe.JoystickButtonUp += b =>
        {
            if (this.InvokeRequired) this.Invoke((GamepadButtonEvent)gpe_JoystickButtonUp);
            else gpe_JoystickButtonUp(b);
        };
        var thread = new Thread(gpe.Run);
        thread.Start();

        base.OnLoad(e);
    }

    void gpe_JoystickButtonUp(SharpDX.XInput.GamepadButtonFlags button)
    {
        throw new NotImplementedException();
    }

    void gpe_JoystickButtonDown(SharpDX.XInput.GamepadButtonFlags button)
    {
        throw new NotImplementedException();
    }
}

Code of the class that will be running on another thread:

public delegate void GamepadButtonEvent(GamepadButtonFlags button);

public class GamepadEvents
{
    public event GamepadButtonEvent JoystickButtonUp;
    public event GamepadButtonEvent JoystickButtonDown;

    private bool[] buttonsBools = new bool[32];

    public void Run()
    {
        while (true)
        {
            var controller = new Controller();
            State s = controller.GetState();

            ButtonSignal(0, GamepadButtonFlags.DPadUp, (s.Gamepad.Buttons & GamepadButtonFlags.DPadUp) != 0);
            ButtonSignal(1, GamepadButtonFlags.DPadDown, (s.Gamepad.Buttons & GamepadButtonFlags.DPadDown) != 0);
            ButtonSignal(2, GamepadButtonFlags.DPadLeft, (s.Gamepad.Buttons & GamepadButtonFlags.DPadLeft) != 0);
            ButtonSignal(3, GamepadButtonFlags.DPadRight, (s.Gamepad.Buttons & GamepadButtonFlags.DPadRight) != 0);
            ButtonSignal(4, GamepadButtonFlags.Start, (s.Gamepad.Buttons & GamepadButtonFlags.Start) != 0);
            ButtonSignal(5, GamepadButtonFlags.Back, (s.Gamepad.Buttons & GamepadButtonFlags.Back) != 0);
            ButtonSignal(6, GamepadButtonFlags.LeftThumb, (s.Gamepad.Buttons & GamepadButtonFlags.LeftThumb) != 0);
            ButtonSignal(7, GamepadButtonFlags.RightThumb, (s.Gamepad.Buttons & GamepadButtonFlags.RightThumb) != 0);
            ButtonSignal(8, GamepadButtonFlags.LeftShoulder, (s.Gamepad.Buttons & GamepadButtonFlags.LeftShoulder) != 0);
            ButtonSignal(9, GamepadButtonFlags.RightShoulder, (s.Gamepad.Buttons & GamepadButtonFlags.RightShoulder) != 0);
            ButtonSignal(10, GamepadButtonFlags.A, (s.Gamepad.Buttons & GamepadButtonFlags.A) != 0);
            ButtonSignal(11, GamepadButtonFlags.B, (s.Gamepad.Buttons & GamepadButtonFlags.B) != 0);
            ButtonSignal(12, GamepadButtonFlags.X, (s.Gamepad.Buttons & GamepadButtonFlags.X) != 0);
            ButtonSignal(13, GamepadButtonFlags.Y, (s.Gamepad.Buttons & GamepadButtonFlags.Y) != 0);

            Thread.Sleep(1);
        }
    }

    private void ButtonSignal(int btnIdx, GamepadButtonFlags gamepadButton, bool pressed)
    {
        bool wasPressed = buttonsBools[btnIdx];
        buttonsBools[btnIdx] = pressed;

        if (wasPressed && !pressed)
            this.JoystickButtonUp(gamepadButton);

        if (!wasPressed && pressed)
            this.JoystickButtonDown(gamepadButton);
    }
}
  • This works perfectly well. With a sufficiently small interval between control status checks (I use 10-15ms), I can treat push button and push button cases repeatedly as the same thing.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.