A Joystick Control






3.14/5 (6 votes)
May 31, 2005
3 min read

55051

2665
A joystick control using a simplified polar coordinate system to return an orientation and magnitude, and a custom Vehicle UserControl with an Offset method that uses the said coordinate system.
Introduction
This project provides a joystick control that uses a simplified polar coordinate system (i.e., only the eight grossest compass points for directions — north, northeast, east, southeast, etc.). It also provides a custom vehicle class that can take an offset in the compass coordinate system that the joystick uses.
Background
The control uses a trig function to arrive at the angle of the joystick-- FindOrientation
uses Math.Atan2(double, double)
to get the angle in radians that the joystick ray makes to the x axis. Don't be frightened though--the Joystick actually returns a member of the enumeration below instead of an angle for its orientation, and you can use simple Euclidean geometry to translate that and the magnitude (radial distance of the mouse pointer from the center of the joystick) into x and y offsets to feed to some (hopefully) moving object. Of course, if I go on and produce version 0.2 that returns orientation in radians, you'll need some more trig to deal with it.
There are actually two custom controls in the project--the Joystick
and the Vehicle
. The latter is just a UserControl
that has a custom Offset
method that takes an orientation and a magnitude rather than the usual x and y components of motion. A vector, if you will, and are thinking calculus and not STL.
Using the code
If you want to use the JoyStickControl
in your own project, you will have to deal with the polar coordinate system that it uses. Perhaps I should mention here that the control is the result of a school assignment—left to my own, I would have used either plain x-y coordinates, or a full polar coordinate system, with the orientation being passed in radians. As it is, the joystick contains a public enumeration:
public enum compassPoints
{
north,
northeast,
east,
southeast,
south,
southwest,
west,
northwest
}
which has all the possible values for orientation that it may pass through its MouseMoving
event. In my own use of the thing, I had the main form subscribe to the custom event; the form then calls the Vehicle
's Offset
method, which deals with the coordinates like so:
public void Offset(Joystick.compassPoints orientation, int magnitude)
{
this.orientation = orientation;
this.magnitude = (double)magnitude;
switch (orientation)
{
case Joystick.compassPoints.north :
this.Location = new Point(this.Location.X,
this.Location.Y - (int)this.magnitude);
break;
case Joystick.compassPoints.northeast :
this.Location = new Point(this.Location.X +
((int)Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2))/2), 0)),
this.Location.Y +
(int)-Math.Round((Math.Sqrt((Math.Pow(this.magnitude, 2))/2)), 0));
break;
case Joystick.compassPoints.east :
this.Location =
new Point(this.Location.X + magnitude, this.Location.Y);
break;
case Joystick.compassPoints.southeast :
this.Location = new Point(this.Location.X +
(int)Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2))/2), 0),
this.Location.Y +
(int)Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2)/2)), 0));
break;
case Joystick.compassPoints.south :
this.Location =
new Point(this.Location.X, this.Location.Y + (magnitude));
break;
case Joystick.compassPoints.southwest :
this.Location = new Point(this.Location.X +
(int)-Math.Round(Math.Sqrt(Math.Pow(this.magnitude, 2)/2), 0),
this.Location.Y +
(int)Math.Round((Math.Sqrt((Math.Pow(this.magnitude, 2))/2)), 0));
break;
case Joystick.compassPoints.west :
this.Location =
new Point(this.Location.X - (magnitude), this.Location.Y);
break;
case Joystick.compassPoints.northwest :
this.Location = new Point(this.Location.X +
(int)-Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2))/2), 0),
this.Location.Y +
(int)-Math.Round(Math.Sqrt((Math.Pow(this.magnitude, 2))/2), 0));
break;
default :
break;
}
}
In other words, we just use the compass points and a bit of simple Euclidean geometry, (opposite)2 + (adjacent)2 = (hypotenuse)2, to arrive at a new location. Simplicity itself, though with lots of typing for me. For reasons of precision, I do all my calculations using double
s, and then carefully round them before casting them to int
s, since the cast would otherwise simply strip the decimal places off without rounding, producing sloppy results.
Points of Interest
While working on this, I discovered that the Joystick.MouseMove
event fired continuously while the mouse was over the control. I don't know whether or not this is intentional on Microsoft's part, or whether I've found a bug. The MSDN simply states "Occurs when the mouse pointer is moved over the control," which sounds like it should mean that it either fires when the mouse moves over, or enters the control (but this of course would be redundant, since controls already have a MouseEnter
event), or that it fires when the mouse moves over the control--moves while within the control. Instead, the thing just fires continuously while the mouse is inside the thing. I ended up having the Joystick
screen out the noise by checking to see that the mouse had actually moved before firing its custom MouseMovingEvent
, which the form listens to. Movement of the Vehicle
is controlled by a timer on the main form, the form calling the Vehicle
's Offset
method every 18 ms while the mouse is over the Joystick
, and what with the Joystick.MouseMovingEvent
originally firing continuously in response to its own MouseMove
event (firing 87 bazillion times a second) the poor timer never had a chance to do anything until I added my screening code.