Nuclex.Input Duplicate devices

Feb 11, 2011 at 3:27 AM

Hi, I have just started playing with this framework, and I have noticed that if you connect an x-box controller and read Gamepad one and five they have the same states raised. I have noticed that this does not occur if you plug the controller in after the program has started, but does if the controller is connected to the pc before it starts.

Could you confirm if this is an issue or if it is just my lack of understanding/poor code.


Cheers

Phil

Below is the modified Nuclex.Input.Demo that i used to test/demo the issue.

 

 

using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Nuclex.Input.Devices;
using Nuclex.Support;

namespace Nuclex.Input.Demo {

  /// <summary>Game that demonstrates the usage of Nuclex.Input</summary>
  public class InputDemoGame : Microsoft.Xna.Framework.Game {

    /// <summary>Initializes a new input demonstration game</summary>
    public InputDemoGame() {
      this.graphics = new GraphicsDeviceManager(this);
      this.inputManager = new InputManager(Services, Window.Handle);

      Content.RootDirectory = "Content";
    }

    /// <summary>
    ///   Allows the game to perform any initialization it needs to before starting to run.
    ///   This is where it can query for any required services and load any non-graphic
    ///   related content. Calling base.Initialize will enumerate through any components
    ///   and initialize them as well.
    /// </summary>
    protected override void Initialize() {
      base.Initialize();

      // Whenever a key is pressed on the keyboard, call the charEntered() method
      // (see below) so the game can do something with the character.
      IKeyboard keyboard = this.inputManager.GetKeyboard();
      keyboard.CharacterEntered += new Devices.CharacterDelegate(charEntered);

      IGamePad gamePad = this.inputManager.GetGamePad(PlayerIndex.One);
      gamePad.ButtonPressed += new GamePadButtonDelegate(gamePadButtonPressed);
    }

    /// <summary>Called when a character is entered on the keyboard</summary>
    /// <param name="character">Character that has been entered</param>
    private void charEntered(char character) {
      if (character == '\b') { // backspace
        if (this.userInputStringBuilder.Length > 0) {
          this.userInputStringBuilder.Remove(this.userInputStringBuilder.Length - 1, 1);
        }
      } else {
        this.userInputStringBuilder.Append(character);
      }
    }

    /// <summary>Called when the player presses a button on the first game pad</summary>
    /// <param name="buttons">Button(s) that have been pressed</param>
    private void gamePadButtonPressed(Microsoft.Xna.Framework.Input.Buttons buttons) {

      // End the game if the player has pressed the Back button
      if ((buttons & Microsoft.Xna.Framework.Input.Buttons.Back) != 0) {
        Exit();
      }

    }

    /// <summary>
    ///   LoadContent will be called once per game and is the place to load
    ///   all of your content.
    /// </summary>
    protected override void LoadContent() {

      // Create a new SpriteBatch, which can be used to draw textures.
      this.spriteBatch = new SpriteBatch(GraphicsDevice);

      // Load the font we'll be using for the text display
      this.displayFont = Content.Load<SpriteFont>("DisplayFont");

    }

    /// <summary>
    ///   UnloadContent will be called once per game and is the place to unload
    ///   all content.
    /// </summary>
    protected override void UnloadContent() {
      Content.Unload();

      if (this.spriteBatch != null) {
        this.spriteBatch.Dispose();
        this.spriteBatch = null;
      }
    }

    /// <summary>
    /// Allows the game to run logic such as updating the world,
    /// checking for collisions, gathering input, and playing audio.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Update(GameTime gameTime) {
      base.Update(gameTime);

      // Update the status of all input devices
      this.inputManager.Update();

      // Retrieve the state of the first connected Xbox 360 game pad
      // (Hint: DirectInput devices are at index 5-8 if you want to try)
      IGamePad firstGamePad = this.inputManager.GetGamePad(ExtendedPlayerIndex.One);
      IGamePad fifthGamePad = this.inputManager.GetGamePad(ExtendedPlayerIndex.Five);
      this.firstState = firstGamePad.GetExtendedState();
      this.fifthState = fifthGamePad.GetExtendedState();
    }

    /// <summary>
    /// This is called when the game should draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    protected override void Draw(GameTime gameTime) {
      GraphicsDevice.Clear(Color.CornflowerBlue);

      spriteBatch.Begin();
      {
        // Print out the state of the game pad
        printGamePadState();

        // Display the text the user has entered so far
        printKeyboardInput();
      }
      spriteBatch.End();

      base.Draw(gameTime);
    }

    /// <summary>what the user has entered on the keyboard so far</summary>
    private void printKeyboardInput() {
      spriteBatch.DrawString(
        this.displayFont, "Text input:", new Vector2(50, 425), Color.LightGreen
      );

      Vector2 size = this.displayFont.MeasureString("Text input: ");
      spriteBatch.DrawString(
        this.displayFont, this.userInputStringBuilder,
        new Vector2(50 + size.X, 425), Color.LightGreen
      );
    }

    /// <summary>Displays the current state of the game pad</summary>
    private void printGamePadState()
    {

        // Device name
        {

            string x = "";
            foreach (ExtendedPlayerIndex player in ExtendedPlayerIndex.GetValues(typeof(ExtendedPlayerIndex)))
            {
                x = x + "Device:" + player.ToString() + " Name:" + inputManager.GetGamePad(player).Name + " ";

                ExtendedGamePadState state = this.inputManager.GetGamePad(player).GetExtendedState();
                x = x + "Number of buttons:" + state.ButtonCount.ToString() + " ";

                for (int index = 0; index < state.ButtonCount; ++index)
                {
                    if (state.IsButtonDown(index))
                    {
                        x = x + "X";
                    }
                    else
                    {
                        x = x + ".";
                    }
                }
                x = x + "\n";
            }

            x = x + "FIRST CHECK";
            for (int index = 0; index < firstState.ButtonCount; ++index)
            {
                if (firstState.IsButtonDown(index))
                {
                    x = x + "X";
                }
                else
                {
                    x = x + ".";
                }
            }
            x = x + "\n";

            x = x + "FIFTH CHECK";
            for (int index = 0; index < fifthState.ButtonCount; ++index)
            {
                if (fifthState.IsButtonDown(index))
                {
                    x = x + "X";
                }
                else
                {
                    x = x + ".";
                }
            }
            x = x + "\n";
            spriteBatch.DrawString(displayFont, x, new Vector2(50, 50), Color.Yellow);
        }
    }

    /// <summary>Initializes and manages the graphics device used for rendering</summary>
    private GraphicsDeviceManager graphics;
    /// <summary>Batches sprites and text for efficient rendering</summary>
    private SpriteBatch spriteBatch;
    /// <summary>Polls input devices and allows their state to be queried</summary>
    private InputManager inputManager;
    /// <summary>Font used to display text on the screen</summary>
    private SpriteFont displayFont;

    /// <summary>Used to store the most recent state of the game pad</summary>
    private ExtendedGamePadState firstState;
    private ExtendedGamePadState fifthState;
    /// <summary>Temporary string builder used for various purposes</summary>
    private StringBuilder tempStringBuilder = new StringBuilder();
    /// <summary>Contains the text the user has entered on the keyboard</summary>
    private StringBuilder userInputStringBuilder = new StringBuilder();

  }

} // namespace Nuclex.Input.Demo




 

 

Coordinator
Feb 11, 2011 at 10:31 AM
Edited Feb 11, 2011 at 10:31 AM

Your code is fine, this is most likely an issue with Nuclex.Input accessing the game pad under both the XInput and the DirectInput APIs (normally it filters out those DirectInput devices that already have an equivalent XInput device).

I've replaced the code for detecting XInput devices with a snippet provided by the ZMan (http://www.thezbuffer.com/articles/351.aspx) in the latest build.

Could you try it again with this build? You can download daily builds from my CI server here: https://devel.nuclex.org/teamcity/viewLog.html?guest=1&buildId=lastSuccessful&tab=artifacts&buildTypeId=bt13.

Feb 11, 2011 at 12:24 PM

Hi Cygon! Thanks for the very prompt reply! Yeah, the new build  fixed that issue.  I'll start coding my game up right away!

One other thing I did notice with the new build is that if the non-xbox controller is not plugged in before the program is executed, and is subsequently plugged in, it does not register as a device. I think this is a really mior issue from my point of view. Just thought I would raise it just in case somebody else needs it fixed. The above code demonstrates this issue.

Thanks again for the fix and a great product!

Cheers

Phil.