Now that the XBOX 360 controller works on windows I thought it would be fun to hook it up to a windows forms application using Visual C# Express and Managed DirectX (MDX). So here is the scenario; you’re at your desk entering the latest budget numbers into your enterprise accounting system and think, boy it would be nice if I could use the XBOX 360 controller to do this. Ok, so maybe that is not the target scenario for this application.

 

Download the Code :  XBOX360ControllerTest.zip

 

 

XBOX 360 Controller Test

This is a little test harness application to learn how to use the new XInput API. The application lights up the buttons as you push them.

XInput Class

Once you open the solution you can see that there are 2 projects; 1 for the test app and the other is a class library called XInput. The XInput project is a wrapper I created around the DirectX XInput API. This allows you to easily add controller support to any winform application by just referencing the XInput project. This project is a simple C# class library that has one reference added to the Microsoft.DirectX.dll version 2.0.0.0. It contains a static property that holds the state of the controller and a static method that updates the state. UpdateGameControllerState takes a snapshot of the current controller state. This state is then queried for the various button presses. I hard-coded this sample app to only look for 1 controller but it should be easy to modify to add support for the other three controllers. I didn’t add it because I only have 1 controller to test with. But if you have more, the controllers are enumerated in the GetState() method which takes a 0-3 for each of the 4 controllers.

 

namespace XInput

{

    public class GameController

    {

        public static State state;

 

        public static void UpdateGameControllerState()

        {

            // Processing the Controller Input

 

            //TODO: This assumes only 1 controller is connected

            state = Controller.GetState(0);

 

            if (!state.IsConnected) throw new Exception("Controller is not connected");

        }

 

Then next part of the Controller class wraps the state of the controller. After the state is updated you can query the state of individual buttons. For example if the ‘A’ button is pushed calling GameController.AButton will return true.

 

#region Controller State

public static bool AButton { get { return state.GamePad.AButton; } }

public static bool BackButton { get { return state.GamePad.BackButton; } }

public static bool BButton { get { return state.GamePad.BButton; } }

//public static GamePadButtons Buttons { get{ return state.GamePad.Buttons; } }

public static bool DownButton { get { return state.GamePad.DownButton; } }

public static bool LeftButton { get { return state.GamePad.LeftButton; } }

public static bool LeftShoulderButton { get { return state.GamePad.LeftShoulderButton; } }

public static bool LeftThumbButton { get { return state.GamePad.LeftThumbButton; } }

public static short LeftThumbX { get { return state.GamePad.LeftThumbX; } }

public static short LeftThumbY { get { return state.GamePad.LeftThumbY; } }

public static byte LeftTrigger { get { return state.GamePad.LeftTrigger; } }

public static bool RightButton { get { return state.GamePad.RightButton; } }

public static bool RightShoulderButton { get { return state.GamePad.RightShoulderButton; } }

public static bool RightThumbButton { get { return state.GamePad.RightThumbButton; } }

public static short RightThumbX { get { return state.GamePad.RightThumbX; } }

public static short RightThumbY { get { return state.GamePad.RightThumbY; } }

public static byte RightTrigger { get { return state.GamePad.RightTrigger; } }

public static bool StartButton { get { return state.GamePad.StartButton; } }

public static bool UpButton { get { return state.GamePad.UpButton; } }

public static bool XButton { get { return state.GamePad.XButton; } }

public static bool YButton { get { return state.GamePad.YButton; } }

#endregion

 

Controller Test Form

The second project in the solution is called XBOX360_ControllerTest. This project is a simple winforms application that has 1 form called ControllerTestForm. ControllerTestForm has a background image of the controller in the “off” state (as shown above). The image is a front view and a top view of the controller. I actually have much higher resolution images but I turned them down to make this sample as small as possible to download. I edited all of the graphics using Microsoft Digital Image Pro 2006.

 

UpdateController

The UpdateController method first updates the state of the controller by calling the UpdateGameControllerState of the XInput project that we looked at above.

public void UpdateController()

{

    //Update the controller

    try

    {

        GameController.UpdateGameControllerState();

    }

    catch (Exception ex)

    {

        //Catch the controller not connected exception

    }

 

 

PictureBox

The “on” states of the controller are represented by picturebox controlls on the form. So if the “A” button is true then I set the visibility of the “A” button picturebox to true. In the image below we can see the state of the buttons “off” and the state of the buttons “on.”

Now that the controller state has been updated we can check the state of the buttons and set the picturebox controls visibility accordingly.

//Show the buttons pushed

AButton.Visible = GameController.AButton;

BButton.Visible = GameController.BButton;

XButton.Visible = GameController.XButton;

YButton.Visible = GameController.YButton;

 

//Update Start and Back Buttons

StartButton.Visible = GameController.StartButton;

BackButton.Visible = GameController.BackButton;

 

Two Thumbs up

The two thumb sticks, left and right, are analog controls that can be pushed like a button but more commonly they are moved in a direction. The buttons were easy as there are only two states that the button can have. Unlike the buttons, the thumbstick can be any direction and amount from 0, the center, to 32,767. For example if you push the thumbstick all the way to the right then the x value would be 32,767 and the y would be 0. We can can see here in the diagram the orentation and possible values for x and y.

My first thought was to have a image of an arrow that I would turn to point in the direction that the thumbstick was pushed, like a compass needle. I quickly realized that the picturebox control cannot be rotated. I spent a few hours looking for ways to rotate my arrow but none of them really worked like I had hoped. In the process of investigating how to rotate the picturebox I thought of a better idea. It turns out that in order to rotate a picturebox you need to create a custom control that derives from picturebox. In this custom control you must override the paint method and do the work yourself. Ok I was making progress but the rotating arrow doesn’t truly represent the state of the thumstick because it is a vector with length and direction. Keeping my custom picturebox control I now draw a single yellow line from the center. The more you push the thumbstick the longer the line gets.

    /// <summary>

    /// Custom PictureBox to draw the state of the Thumb Buttons

    /// </summary>

    class RotatingPictureBox : System.Windows.Forms.PictureBox

    {

        private Point endPoint = new Point(0, 0);

        private float RadiusMultiplier = 0; //used to adjust the line length (assumes square)

        private float MaxInputValue = 32767f; //max X value for calculating line length

        private int CenterX, CenterY;

 

        public RotatingPictureBox()

        {

            //make sure all drawing happens in the OnPaint only

            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true);

           

            this.Resize += new EventHandler(RotatingPictureBox_Resize);

        }

 

        void RotatingPictureBox_Resize(object sender, EventArgs e)

        {

            CenterX = (this.Width / 2);

            CenterY = (this.Height / 2);

            RadiusMultiplier = (float)CenterX / MaxInputValue;

        }

 

 

        public void UpdateEndPoint(short X, short Y)

        {

            //multiple to get a max line length of 50 which is the radius of the picture box

            //.001 = 35 / 32767 (radius / max controller X and Y)

            int XPoint = CenterX + (int)(X * RadiusMultiplier);

            int YPoint = CenterY - (int)(Y * RadiusMultiplier);

            endPoint = new Point(XPoint, YPoint);

 

            //redraw when this changes

            this.Invalidate();

            this.Update();

        }

 

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs pe)

        {

            base.OnPaint(pe);

 

            Graphics g = pe.Graphics;

            //Center of the picturebox

            Point centerPoint = new Point(Width / 2, Height / 2);

            //Draw a line to indicate the Length and Direction

            g.DrawLine(new Pen(Color.Yellow, 3.0f), centerPoint, endPoint);

        }

    }

 

The code below updates the yellow line on the thumstick and the pushed state.

//Update Thumb Buttons

RightThumbButtonDirection.UpdateEndPoint(GameController.RightThumbX,

                                                                GameController.RightThumbY);

RightThumbButton.Visible = GameController.RightThumbButton;

 

LeftThumbButtonDirection.UpdateEndPoint(GameController.LeftThumbX,

                                                                GameController.LeftThumbY);

LeftThumbButton.Visible = GameController.LeftThumbButton;

 

Direction Pad

The direction pad can have only left/right and up/down state. So if both left and up are true then it is diagonal up to the left. I was able to reuse my custom picturebox that I created for the thumbsticks by locking the x and y values to the maximum value, 32,767.

 

//Update Direction Pad

//Convert Up/Down Left/Right of the directional pad into X and Y

short DirectionPadX = (short)((GameController.RightButton ? 32767 : 0) +

                                            (GameController.LeftButton ? -32767 : 0));

short DirectionPadY = (short)((GameController.UpButton ? 32767 : 0) +

                                            (GameController.DownButton ? -32767 : 0));

DirectionPad.UpdateEndPoint(DirectionPadX, DirectionPadY);

 

Triggers

The two triggers have values between 0 and 255. So I wanted to represent the fact that the trigger was pulled and also how much it was being pulled. I use two pictureboxes for each trigger, left and right. One to represent the state on or off, which I calculate as “on” if the value is greater than 0. The second picturebox grows based on the value. This can be seen in the image below where the amount is represented by the green line. Also the two shoulder buttons have a simple on or off state which is easy to show.

//Update Shoulder Buttons

LeftShoulder.Visible = GameController.LeftShoulderButton;

RightShoulder.Visible = GameController.RightShoulderButton;

 

//Update Triggers and the amount they are pushed

byte leftTrigger = GameController.LeftTrigger;

byte rightTrigger = GameController.RightTrigger;

double maptoPictureBoxHeight = .39; // map trigger max 255 to picturebox max 100

//Left Trigger

LeftTrigger.Visible = (leftTrigger > 0);

LeftTriggerProgress.Height = (int)(leftTrigger * maptoPictureBoxHeight);

LeftTriggerProgress.Visible = (leftTrigger > 0);

//Right Trigger

RightTrigger.Visible = (rightTrigger > 0);

RightTriggerProgress.Height = (int)(rightTrigger * maptoPictureBoxHeight);

RightTriggerProgress.Visible = (rightTrigger > 0);

 

Let’s get ready to Rumble

The controller also contains two rumble motors, left and right. I wired these up to the triggers so that pulling the left or right trigger starts the left or right motor. The more you pull the trigger the higher the rumble of the motor. The motors can have values from 0 to 65,535. The types are actually short which have values of -32,768 to 32,767, but the internal implementation is a ushort which has values from 0 to 65, 535. In order to get the full range of the motor I double the short value and assign it in an unchecked block. I would like to thank the Zman for pointing this out to me. I map this to the 255 positions of the triggers.

 

//Vibrate the controller using the Triggers

double maptoVibration = 128.49; //Map trigger 255 to max vibration 32767

short leftVibration = (short)(leftTrigger * maptoVibration);

short rightVibration = (short)(rightTrigger * maptoVibration);

GameController.SetVibration(leftVibration, rightVibration);

 

This calls the SetVibration method in the XInput controller class.

//Vibrate the controller

public static void SetVibration(short LeftMotorSpeed, short RightMotorSpeed)

{

    Vibration v = new Vibration();

    unchecked

    {

        //these are actually uShort types so double it and don't check

        v.LeftMotorSpeed = (short)(LeftMotorSpeed * 2); //65,535 max 100%

        v.RightMotorSpeed = (short)(RightMotorSpeed * 2); //65,535 max 100%

    }

        Controller.SetVibration(0, v);

}

 

Update Loop

The last piece of this sample to talk about is the actually the first piece I implemented, the update loop. As in any game I needed a way to continuously check the state of the controller. I decided for simplicity to use a simple DoEvents loop. It gets the job done but those of you with a fast game twitch will see that the app can’t update the button presses fast enough.

static void Main()

{

    //Create and show the form

    ControllerTestForm controllerTestForm = new ControllerTestForm();

    controllerTestForm.Show();

 

    //Game Loop

    while (controllerTestForm.Created)

    {

        //Update the XBOX 360 controller state

        controllerTestForm.UpdateController();

        //Let applications handle messages

        Application.DoEvents();

    }

}

 

What is left to be done?

There are a couple more features on the controller that I didn’t get to yet. I will continue to work on this and create another article. Voice and Sound will be one of the next features I add to this sample. This will give me the ability to use voice from the microphone and play sounds in the headset. Another tweak would be to set the deadzone for the thumbsticks. What you may notice is that the thumsticks are super sensitive and never are at 0,0. So the deadzone will allow you to allow for this.

 

Conclusion

This was a fun application to write. I wrote it over the course of a weekend. I think most of my time was spent on the picturebox custom control. If there is any interest in the VB version of this let me know. One thing that is missing from the DirectX SDK are C# samples for the XInput API. I hope that others find this sample useful until those samples are available.

 

Requirements:

Visual C# Express

DirectX SDK - (February 2006)

XBOX 360 Controller and Drivers

 

Download Code :  XBOX360ControllerTest.zip

 

Paul Stubbs
Program Manager