XNA Game-Themed CS1 Examples (XGC1) Release 2.0 (XNA V3.1) 2/8/2010
Topic: Topic.3.ModulesAndFunctions
Example: Ex_10.MathLibrary

# Math Library Functions

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 how to use pre-existing functions that are built into C#, in order to do more complicated mathematical operations

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 animates the movement of two soccer balls from left to right across the screen.  When the soccer balls go beyond the end of the screen, they "wrap around" and jump back to the left edge of the screen.  The top soccer ball traces out the path of a sine wave, while the bottom soccer ball traces out the path of a cosine wave.  (You may find it useful to search the web for terms like "sine wave", or "sine wave animation" if you'd like more information about these mathematical concepts)  There are only two ways to interact with the game (not counting the standard "Press the Back button to exit" way):

1. By pushing the right thumbstick up or down, you can increase or decrease the amplitude of the waves, meaning that you can make the waves (vertically) bigger or smaller.

2. By pushing the right thumbstick left or right, you can increase or decrease the period of the waves, meaning that you can make the waves (horizontally) longer or shorter.

2. Examining The Program:

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

• Declaring the instance variables and named constants public class Game1 : XNACS1Base {      private XNACS1Circle m_SinSoccer; // soccer ball following the sine function     private XNACS1Circle m_CosSoccer; // soccer ball following the cosine function     private float m_Frequency; // number of complete oscillations to fit into 0-100     private float m_Amplitude; // amplitude of the oscillations     #region Constants for circle/ball definition     private const float SINE_BALL_INIT_Y = 35.0f;     private const float COSINE_BALL_INIT_Y = 15.0f;     private const float BALL_INIT_X = 0.0f;     private const float BALL_INIT_RADIUS = 3.0f;     private const float X_SPEED = 0.8f;     private const float SCALE_FACTOR = 0.1f;     #endregion
• We will need to keep track of both soccer balls.  Since one will be used to demonstrate a sine wave, and one will be used to demonstrate a cosine wave, we will need to declare an instance variable for each one (named so that we can easily keep track of which variable is demonstrating which wave):
private XNACS1Circle m_SinSoccer; // soccer ball following the sine function

private XNACS1Circle m_CosSoccer; // soccer ball following the cosine function

• Next, we'll need to keep track of the frequency and amplitude of the waves, which we will store in two, separate float variables:

private float m_Frequency; // number of complete oscillations to fit into 0-100

private float m_Amplitude; // amplitude of the oscillations

•  Finally, we'll declare the constants that we intend to use.  Both balls will initially be placed the same distance away from the left edge of the screen (BALL_INIT_X), and will both move at the same left-to-right speed (X_SPEED), and when applying player input to adjust the frequency/amplitude, we will use the same scaling factor (SCALE_FACTOR).  However, the sine-tracing soccer ball will initially be placed higher up (at (BALL_INIT_X, SINE_BALL_INIT_Y)) than the cosine-tracing soccer ball ( at (BALL_INIT_X, COSINE_BALL_INIT_Y) )
private const float SINE_BALL_INIT_Y = 35.0f;

private const float COSINE_BALL_INIT_Y = 15.0f;

private const float BALL_INIT_X = 0.0f;

private const float BALL_INIT_RADIUS = 3.0f;

private const float X_SPEED = 0.8f;

private const float SCALE_FACTOR = 0.1f;

• InitializeWorld():  protected override void InitializeWorld(){     World.SetWorldCoordinate(new Vector2(0,0), 100.0f);     m_Frequency = 2.0f; // initially fit 2 cycles into 0 to 100     m_Amplitude = 10.0f; ; // initially oscillate from -10 to 10     m_SinSoccer = InitializeSoccer(SINE_BALL_INIT_Y);     m_CosSoccer = InitializeSoccer(COSINE_BALL_INIT_Y); }
• We will pick reasonable values for the frequency and amplitude.  These seem to work pretty well, aesthetically-speaking:
m_Frequency = 2.0f; // initially fit 2 cycles into 0 to 100

m_Amplitude = 10.0f; ; // initially oscillate from -10 to 10

• After that, we'll need to create an initialize both soccer balls, which we do with the aid of the InitializeSoccer function:
m_SinSoccer = InitializeSoccer(SINE_BALL_INIT_Y);

m_CosSoccer = InitializeSoccer(COSINE_BALL_INIT_Y);

• InitializeSoccer(): /// /// Allcoate memory and initialize soccer at different y positions /// /// The y-position /// An initialized Soccer with proper texture private XNACS1Circle InitializeSoccer(float yPos) {     // initialize the soccer ball     XNACS1Circle aSoccer = new XNACS1Circle();     aSoccer.Center = new Vector2(BALL_INIT_X, yPos);     aSoccer.Radius = BALL_INIT_RADIUS;     aSoccer.Texture = "SoccerBall";     return aSoccer; }
• At this point, you've seen this sort of function several times - it creates a new XNACS1Circle object, storing a reference to that new object into the local variabe aSoccer. It then initializes the new object based on the parameter and various named constants, and the returns a reference to that object back to the InitializeWorld method.
• UpdateWorld(): protected override void UpdateWorld(){     if (GamePad.ButtonBackClicked())         this.Exit();     // 1. change the period and amplitude according to right thumb stick     m_Amplitude += SCALE_FACTOR * GamePad.ThumbSticks.Right.Y;     m_Frequency += SCALE_FACTOR * GamePad.ThumbSticks.Right.X;     // 2. Compute a new X position based on SinSoccer's centerX     float newX = (m_SinSoccer.CenterX + X_SPEED) % World.WorldMax.X;     m_SinSoccer.CenterX = newX;     m_CosSoccer.CenterX = newX;     // 3. convert the newX value into a radian value     float xInRad = (newX / World.WorldMax.X) * ((float) Math.PI * 2.0f) * m_Frequency;     // 4. compute the y values for the two soccer balls     m_SinSoccer.CenterY = SINE_BALL_INIT_Y + (m_Amplitude * (float) Math.Sin(xInRad));     m_CosSoccer.CenterY = COSINE_BALL_INIT_Y + (m_Amplitude * (float) Math.Cos(xInRad));     // 5. after calling the above, the two radii should be the same?!     EchoToTopStatus("Amplitude=" + m_Amplitude +         " NumPeriod=" + m_Frequency); }
• This version of UpdateWorld follows the same sort of pattern we've seen in recent, previous tutorials: collect up input from the user, calculate the new location of the on-screen soccer balls, move the soccer balls to those new locations, and then update the message at the top/bottom of the screen.  Let's look at those steps in more detail.
• After the standard "does the player want to exit the game?" check, we scale the right thumbstick's input (otherwise the input will be too large, and the amplitude/frequency will be changed too quickly), and assign that scaled input to the appropriate instance variable:
// 1. change the period and amplitude according to right thumb stick

m_Amplitude += SCALE_FACTOR * GamePad.ThumbSticks.Right.Y;

m_Frequency += SCALE_FACTOR * GamePad.ThumbSticks.Right.X;

• Note that when using something like +=, we do everything to the right of the +=, then do the +=.  In other words, this:
m_Amplitude += SCALE_FACTOR * GamePad.ThumbSticks.Right.Y;
is equivalent to this:
m_Amplitude += (SCALE_FACTOR * GamePad.ThumbSticks.Right.Y); // Do stuff in parens FIRST, then do everything else
• Next, we want to figure out how far the balls have moved on the X axis.  Basically, we've going to move them rightwards by a distance of X_SPEED, and if they've gone off the screen, we'll go back to the left edge.  This can be done directly using the 'modulus', or 'remainder' operator, which also works for floating-point numbers, as well as integer numbers:
// 2. Compute a new X position based on SinSoccer's centerX

float newX = (m_SinSoccer.CenterX + X_SPEED) % World.WorldMax.X;

• Once we've got that number, we assign it to the X part of the center of each circle:
m_SinSoccer.CenterX = newX;

m_CosSoccer.CenterX = newX;

• Since the C# library assumes that we'll be passing in an argument whose unit is radians, we'll need to convert our data to radians, first:
// 3. convert the newX value into a radian value

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

• After having done that, we actually call the sine and cosine functions, and use the output to assign the new height of the circles:
// 4. compute the y values for the two soccer balls

m_SinSoccer.CenterY = SINE_BALL_INIT_Y + (m_Amplitude * (float) Math.Sin(xInRad));

m_CosSoccer.CenterY = COSINE_BALL_INIT_Y + (m_Amplitude * (float) Math.Cos(xInRad));

• Lastly, we output a message to the top of the screen:
// 5. after calling the above, the two radii should be the same?!

EchoToTopStatus("Amplitude=" + m_Amplitude +

" NumPeriod=" + m_Frequency);

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. Trying out other mathematical functions
For this exercise, you should use the same project that was explained in the above tutorial.
Modify the provided solution so that your program demonstrates the use of several common mathematical functions, that are built into the core C# / .Net framework.  For each one, follow the pattern set forth in the tutorial's code, and set the Y value of the center of one of the circles equal to the output from each of the below functions, one at a time.  The goal for this exercise is to give you the opportunity to look up various math functions, and make use of them here, in a graphical manner.
• Math.Sqrt
• Math.Round
This method will round off a number to the nearest integer.  (You can also use it to round off after a certain number of decimal places, but that's more than this exercise is asking you to do)
• Math.Ceiling
This method will round up.
• Math.Floor
This method will round down.  Note that while this accomplishes the same sort of task as casting a number to an integer (i.e., digits after the decimal place will be removed), this method will NOT convert the number to an integer.  So you should use this if you want to round something down, but you want to keep your calculations as a double-precision number.
• HINT: It may be useful to look these up online.  The following seems to be a good resource: http://msdn.microsoft.com/en-us/library/system.math_methods.aspx, but you may find other resources online, too.

Project home page: The Game-Themed Introductory Programming Project.
 Kelvin Sung Computing and Software Systems University of Washington, Bothell ksung@u.washington.edu Michael Panitz Business And Information Technology Cascadia Community College mpanitz@cascadia.eduu

 This work is supported in part by a grant from Microsoft Research under the Computer Gaming Curriculum in Computer Science RFP, Award Number 15871 and 16531. 2/8/2010