Monday, April 6, 2009

Using the Mouse to Rotate a Silverlight Element

Steve: I was asked on two separate occasions, during interviews, programming problems that involved geometry -- in other words, could I work with sin, cos and tan to calculate geometry. This is something I learned in 8th grade and I have used this knowledge in many applications, but this demo is probably the most fun.

The goal of this demo, as explained in the video below, is to build a targeting system that uses mouse movement to direct the projectiles.



The XAML in the tank control needs to represent the turret separate from the body of the tank; Deb has used Expression Blend to create a transform group that lets the turret rotate separately from the tank.



Now it is time to overlay the math on the geometry:



Call the code below every time the mouse moves; the variables synchronize with the picture:

e.GetPosition(oElement).X and e.GetPosition(oElement).Y get the current position
of the mouse in the coordinate system of the canvas.

oControl is the user control that is the tank on the screen, so oSize is the height and width of the tank, and oPoint is where the center of the tank is relative to the top-left corner and is used to position it on the canvas.

TranslationX and TranslationY is current location of the tank's top-left corner on the canvas, and TranslationAngle is the direction it is heading.

Now that math. Dx an Dy represent the distance along the X and Y axes from the center of the tank to the current mouse location. The calculations are done in the coordinate system of the canvas.

The radius or hypotenuse of the triangle is calculated and then divided into Dx and Dy to normalize the values. Then the ArcTan is calculated to determine the angle.

One final trick. Remember from the first diagram that the rotation of the turret is relative to the body of the tank. To orient the gun so it points at the mouse, you must subtract the calculated angle from the direction the tank is traveling.

Call the code below when the mouse is moved:


////virtual method that lets any game element fire a weapon
public virtual bool TrackMouse(Canvas oElement, Control oControl, MouseEventArgs e)
{
Size oSize = oControl.RenderSize;
Point oPoint = oControl.RenderTransformOrigin;

//some basic trig
double dX = e.GetPosition(oElement).X - TranslationX + oPoint.X * oSize.Width;
double dY = TranslationY + oPoint.Y * oSize.Height - e.GetPosition(oElement).Y;
double dRadius = Math.Sqrt(dX * dX + dY * dY);

//remember WPF wants angles in degrees
double dAngle = System.Math.Atan2(dY / dRadius, dX / dRadius) * 180.0 / System.Math.PI;
dAngle %= 360.0;

//the virtual SetFireAngle method is used to control the turret
//this code is here to account for the direction the tank is heading (i.e. Player.TranslationAngle)
//allowing the tank to move in one direction and shoot in another
SetFireAngle(TranslationAngle - dAngle);
return false;
}

No comments: