XNA Game-Themed CS1 Examples (XGC1) Release 2.0 (XNA V3.1) 2/8/2010
Topic: Topic.5.RepetitionStructures
Example: Ex_3.ExampleWithWhile

# Repetition: Drawing A Sine Curve With A While Loop

References:

• Pre-requisite: it is assumed that you have read through the prior tutorials, and are familiar with the concepts covered in those tutorials.

Goals:

• In this tutorial, we will:
• Examine a simple while loop, which is used to repeat some action a large number of times

1. Obtain the example code

Download and unzip the zip file and you will see an ExampleProgram folder. Open the ExampleProgram folder, the EXE folder contains the compiled program and you can double click on the .sln file to work with the source code.

When the game starts, you'll see a screen that looks similar to this:

As you can see, this program draws a 'wave' of small soccer balls across the screen, plus a larger basket. You can move the basketball using the right thumbstick, and you change the amplitude (height) of the wave using the left thumbstick's Y part, and the frequency (how many waves on the screen at once) using the left thumbstick's X part.

We will get the wave using the mathematical sine function, which can be graphed like so:

Notice how the soccer balls in the screenshot follow the same pattern as the blue wave line in the above picture.  Essentially, if you plug lots of values (between 0 and 6.28318...  ( 2 * π, really) into the sine function, and then graph the y values that you get out, you'll get the above picture.  If you've taken trigonometry, then this is all review.  If you haven't yet taken trigonometry, then the key things to remember are that the 'amplitude' is the height of the wave, and that the 'frequency' is the number of times the wave is drawn in a given distance, more-or-less.  A fuller explanation of the sine function is beyond the scope of this tutorial.

2. Examining The Program:

Let's examine the C# source code that produces the behavior we see on-screen

• InitializeWorld():  // initialize the frequency and amplitude of the sine curve     m_Frequency = 2.0f; // initially fit 2 cycles into 0 to 100     m_Amplitude = 10.0f; ; // initially oscillate from -10 to 10
• When we initialize the game, we need to pick starting values for the m_Frequency and m_Amplitude instance variables.  We choose 2.0f for the frequency, and 10.0f for the amplitude.
• Other work is done here, as well, which you should be able to understand on your own.
• The basic approach that we're using in this method is this:
1. Remove all the current soccer balls from the screen
2. Create a new set of brand-new soccer balls on the screen
Just like in the previous tutorial, this is memory-inefficient (even more so than the previous tutorial, since we'll be generating many more circle objects in this tutorial).  Also, since we want the basketball to keep being drawn on the screen, we'll need to first remove everything from the drawing set, then later re-add the basketball so it stays on-screen.
• Notice that in the previous tutorial, we could have created three instance variables for the three soccer balls if we had really wanted to.  For this tutorial, there far too many soccer balls to manually declare a variable for each of them.  So our 'remove everything, then create a large number of new soccer balls' strategy is more useful here.
• Let's examine the details of how the loop operates by tracing through several iterations of the loop, in detail.
1. While it's not technically part of the loop itself, the following line creates two variables: xPos to track the current x value that we're going to examine, and yPos will store the corresponding value that we get from the sine function.  Notice that we don't bother initializing the yPos variable, since we will assign it a value before we use it.  Since C# will give us a compile-time error if we try to use it before giving an initial value, we will list it as being <uninitialized> until it's assigned a value (so that we know when it's been initialized)
float xPos = 0.0f, yPos;
 Variable Name Value xPos 0.0f yPos < uninitialized >
2. Next, the program checks the condition of the while loop:
while (xPos < World.WorldMax.X)
Since xPos has the value 0.0f, and this is clearly less than the 100.0f that decided to use as the width of the screen (back in the constructor), the condition evaluates to true.

None of the variables' values have changed.
3. Next, the body of the loop is executed:
1. The first thing we do is convert the xPos variable into radians:

float radian = (xPos / World.WorldMax.X) * (2.0f * ((float)Math.PI)) * m_Frequency;

In a nutshell, 0 /World.WorldMax.X is zero, and zero times anything is still zero, so radian will end up being zero.
Once we've done that, we've got:

 Variable Name Value xPos 0.0f yPos < uninitialized > radian 0.0f
2. Next, we make the call to the sine function, then scale that up by the amplitude.  After that, we shift the whole thing up by     BALL_INIT_Y, otherwise the part of the that has a negative Y value will be below the bottom of the screen:

yPos = BALL_INIT_Y + (m_Amplitude * ((float)Math.Sin(radian)));

This gives us:

 Variable Name Value xPos 0.0f yPos 30.0f radian 0.12566
3. Once we've got the Y value, we can create a new, small soccer ball at the point (0.0f, 30.0f)

CreateABallAt(xPos, yPos, "SoccerBall");

4. Lastly, we increment xPos by

xPos = xPos + 1;

So at the end of the first iteration of the loop, we've got:

 Variable Name Value xPos 1.0f yPos 30.0f radian 0.12566
4. The program then returns to the line
while (xPos < World.WorldMax.X)
Since xPos has the value 1.0f, and this is clearly less than 100.0f, the condition evaluates to true.
• This loop will execute 100 times, since xPos is incremented by one each time through the loop, and World.WorldMax.X has the value 100.
• You'll notice that this code is another example of a counting loop, like we saw in the prior example - we start xPos at zero, we increment it by 1 each time through the loop, and the condition specifies that the loop should keep iterating until xPos has reached 100.
• You'll notice that we're also using xPos as the X value of the current soccer ball, in addition to serving as a counter.  (In contrast, in the previous tutorial, the variable counter ONLY served to keep track of the current count)
5. Next, the body of the loop is executed a second time
1. The first thing we do is convert the xPos variable into radians:

float radian = (xPos / World.WorldMax.X) * (2.0f * ((float)Math.PI)) * m_Frequency;

Once we've done that, we've got:

 Variable Name Value xPos 1.0f yPos 0.12533f radian 0.12566
2. Next, we make the call to the sine function, then scale that up by the amplitude.  After that, we shift the whole thing up by     BALL_INIT_Y, otherwise the part of the that has a negative Y value will be off the bottom of the screen:

yPos = BALL_INIT_Y + (m_Amplitude * ((float)Math.Sin(radian)));

This gives us:

 Variable Name Value xPos 0.0f yPos 31.2533f radian 0.12566
3. Once we've got the Y value, we can create a new, small soccer ball at the point (0.0f, 31.2533f)

CreateABallAt(xPos, yPos, "SoccerBall");

4. Lastly, we increment xPos by

xPos = xPos + 1;

So at the end of the first iteration of the loop, we've got:

 Variable Name Value xPos 2.0f yPos 31.2533f radian 0.12566
6. The program then returns to the line
while (xPos < World.WorldMax.X)
Since xPos has the value 1.0f, and this is clearly less than 100.0f, the condition evaluates to true.
7. The loop will execute 98 more times, thus drawing 100 soccer balls on the screen.  We won't trace through all iterations of the loop here, but at this point, you should be able to trace through as many iterations as you want (or are asked to do)
• Once the loop has finished drawing the soccer balls onto the screen, we add the basketball back onto the screen, move it around based on player input, and then echo the current amplitude/frequency to the bottom of the screen.
• Notice that since the basketball is referred to using a variable, we can simply move it, rather than having to create another one (like we did with the soccer balls).

FURTHER EXERCISES::

1. Start from a blank starter project (1000.201, if you need it), and re-do the code from memory as much as possible.  On your first try, do what you can, and keep the above code open so that when you get stuck, you can quickly look up what you forgot (and that after you finish a line, so that you can compare your line to the 'correct' line).  On the next try, do the same thing, but try to use the finished code less.  Repeat this until you can type everything, without refering the tutorial's code.
• Repeat this exercise daily for several days, so that you really get the hang of this.  As you go on, periodically review this by re-doing this exercise.
2. Familiarizing Yourself With Loops: Identifying Errors
Given the following code, what is wrong with the loop?  Without running the program, what will happen when the program is executed?  How would you fix it?
 float xPos = 0.0f, yPos;         // here is the while loop     while (xPos > World.WorldMax.X)     {         float radian = (xPos / World.WorldMax.X) * (2.0f * ((float)Math.PI)) * m_Frequency;         yPos = BALL_INIT_Y + (m_Amplitude * ((float)Math.Sin(radian)));         CreateABallAt(xPos, yPos, "SoccerBall");         xPos = xPos + 1;     }
3. Familiarizing Yourself With Loops: Graphing Cosine
For this exercise, you should use the same project that was explained in the above tutorial.
Instead of graphing the sine function, try graphing the cosine function, instead.
4. Familiarizing Yourself With Loops: Sequences
For this exercise, you should use the same project that was explained in the above tutorial.
Try moving the line "m_BasketBall.AddToDrawSet();" to before the loop that plots the sine wave.  What effect does this have on your program?