A practical use for oscillation
The movement of the ball is pretty smooth, but let's take a look at a more practical application.
1. Open the GuitarStrings project to code along with this example. The project contains a guitar body object and a guitar string object. We're going to add six strings to the guitar, and make them vibrate as the mouse passes over each string.
2. We'll start coding up the GuitarString.xaml.es file by declaring some variables. Much like the ball, the GuitarString object has variables for Angle, Range, and Speed. There is also a variable called ResetRange, which is used to keep track of the Range setting for each string (the use of this variable will become clearer in a few moments), as well as a TopX variable. Both Range and TopX are public variables, so they are accessible from the main program. This code goes just before the GuitarString() constructor:
private double Angle = 0; public double Range = 3; private double ResetRange; private double Speed = 2; public double TopX;
3. Inside the GuitarString() constructor, ResetRange is used to store the Range value that is set when the GuitarString object is instantiated from the main code. The GuitarString object has a storyboard timer in it called Vibrate that is used to make the string move, and an event handler is added to the Completed event for this storyboard.
public GuitarString() {
InitializeComponent(); ResetRange = Range;
this.Vibrate.Completed += new EventHandler(Vibrate_Completed);
4. Each string handles its own oscillation movements inside the Completed event handler. When the storyboard completes, a quick check is done to see if the Range value is greater than 0. If so, the string is moved with our familiar oscillation code, and the timer is restarted. Notice that the Range value is dampened with each pass. Real guitar strings do not vibrate endlessly, and neither should ours. By dampening the range, we can make the string vibrate widely when first hit and then fade down to no motion over time.
If the Range has dampened down to 0 or lower, the Vibrate movement is stopped, the angle is reset, and the Range value is reset to the original value in order to prepare for the next time the string needs to move. This code goes inside the event handler, which is placed outside of the MainPage() constructor:
private void Vibrate_Completed(object sender, EventArgs e) {
this.SetValue(Canvas.TopProperty, TopX
Range -= .025; Vibrate.BeginQ;
Vibrate.StopQ;
Range = ResetRange;
5. That's all we need to do for the strings. Now, we need to work on our interface a bit, so open the MainPage.xaml.es file for editing. We already know we're going to need an instance of the guitar body object, as well as six instances of the String object. In addition, we will use the mouse to determine if a string has been plucked, so we need a Point object to store the current mouse position, as well as one to store the last mouse position. All of these variable declarations are placed just before the MainPageQ constructor:
private Guitar MyGuitar = new GuitarQ;
private GuitarString String6 = new GuitarStringQ;
private GuitarString strings = new GuitarStringQ;
private GuitarString String4 = new GuitarStringQ;
private GuitarString String3 = new GuitarStringQ;
private GuitarString String2 = new GuitarStringQ;
private GuitarString Stringl = new GuitarStringQ;
private Point MousePosition; private Point LastMousePosition;
6. Inside the MainPageQ constructor, instantiate all of the objects. Normally, guitar strings are attached to both the bridge and the tuning pegs at the top of the neck, and vibrate in the middle. We are going to cheat a little bit and have the whole string vibrate, so we're going to get close up on the guitar body. As such, our guitar is scaled up to 450% and positioned appropriately before being added to the LayoutRoot Canvas.
Next come the strings. Each has its left and top property set. Notice that the TopX public variable is set along with the top property in order to create the point of origin for the oscillating movement that was created in the GuitarString object. The thickness of guitar strings varies, and since our String object is made with a line stroke, we can adjust the stroke to create strings of varied thickness. We also define a range—the thicker strings of a guitar vibrate more widely than the thinner ones. After each string is set up, it is added to the LayoutRoot Canvas.
The next-to-last line of this section sets the y component of the LastMousePosition variable to be equal to the height of the application. This avoids having the strings play inadvertently when the application is loaded if the mouse is being moved toward the top of the application.
The last line sets up the MouseMoveQ event handler for the LayoutRoot Canvas. The event handler function for MouseMoveQ is where all the action takes place for the application.
public MainPageQ {
InitializeComponentQ;
MyGuitar.GuitarScale.ScaleX = MyGuitar.GuitarScale.ScaleY = 4.5; MyGuitar.SetValue(Canvas.LeftProperty, 600.00); MyGuitar.SetValue(Canvas.TopProperty, 250.00); LayoutRoot.Children.Add(MyGuitar);
String6.SetValue(Canvas.LeftProperty, -10.00); String6.SetValue(Canvas.TopProperty, 265.00); String6.TopX = 265; String6.Range = 5; LayoutRoot.Children.Add(String6);
String5.SetValue(Canvas.LeftProperty, -10.00); String5.SetValue(Canvas.TopProperty, 280.00); StringS.TopX = 280; String6.Range = 4; LayoutRoot.Children.Add(String5);
String4.SetValue(Canvas.LeftProperty, -10.00); String4.SetValue(Canvas.TopProperty, 296.00); String4.StringLine.StrokeThickness = 3; String4.TopX = 296; String4.Range = 4; LayoutRoot.Children.Add(String4);
String3.SetValue(Canvas.LeftProperty, -10.00); String3.SetValue(Canvas.TopProperty, 312.00); String3.StringLine.StrokeThickness = 3; String3.TopX = 312; String3.Range = 3; LayoutRoot.Children.Add(String3);
String2.SetValue(Canvas.LeftProperty, -10.00); String2.SetValue(Canvas.TopProperty, 328.00); String2.StringLine.StrokeThickness = 2; String2.TopX = 328; String2.Range = 3; LayoutRoot.Children.Add(String2);
Stringl.SetValue(Canvas.LeftProperty, -10.00); Stringl.SetValue(Canvas.TopProperty, 344.00); Stringl.StringLine.StrokeThickness = 2; Stringl.TopX = 344; Stringl.Range = 3; LayoutRoot.Children.Add(Stringl);
LastMousePosition.Y = LayoutRoot.Height;
LayoutRoot.MouseMove += new MouseEventHandler(Page_MouseMove);
7. The MouseMove() event handler code is shown in the following listing. The idea here is to only strum a string when the mouse is moved toward the bottom of the application. To do this, we start by getting the current position of the mouse. We can then check the current mouse position against the last mouse position to determine which way the mouse is moving, and if it has encountered the y position where a string is located.
The first check says, "If the mouse is located at 344y or greater, and the last mouse position was less than 344y (meaning it was above the string), then play that string's Vibrate storyboard." Each of the other five strings is checked the same way, with the appropriate y value inserted for the tests.
private void Page_MouseMove(object sender, MouseEventArgs e) {
MousePosition = e.GetPosition(null); if (MousePosition.Y > 344 && LastMousePosition.Y < 344) Stringl.Vibrate.Begin();
f (MousePosition.Y > 328 && LastMousePosition.Y < 328) String2.Vibrate.Begin();
f (MousePosition.Y > 312 && LastMousePosition.Y < 312) String3.Vibrate.Begin();
f (MousePosition.Y > 296 && LastMousePosition.Y < 296) String4.Vibrate.Begin();
f (MousePosition.Y > 280 && LastMousePosition.Y < 280) String5.Vibrate.Begin();
f (MousePosition.Y > 265 && LastMousePosition.Y < 265) String6.Vibrate.Begin();
LastMousePosition = MousePosition;
When the application is run, the guitar and strings are drawn in the application, as shown in Figure 6-24. When the mouse is positioned above the strings and moved downward, the strings will vibrate as they are encountered and continue to oscillate until the range dampening cuts the motion down enough to reset the string for the next pass.
Ideally, a range of y values would be provided to test for each string hit, as it is possible to move the mouse over a string without a hit being registered. In addition, a string should not be reset until the vibration has dampened enough to drop below the threshold set in the user control, so you cannot continuously strum the strings.
- Figure 6-24. The guitar strings oscillate as the mouse is moved across them. The GuitarStringsCompleted project contains the final version of the code for this project.
Post a comment