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:
In this tutorial, the program that you can run closely
resembles the program that you ran in the previous tutorial. What's
different is the message at the top of the screen. The program displays
the sum of all the hit counts as "total=341", followed by the sum of one half of
all the hit counts (in this case, 168). Similarly, the average is
calculated on all the hits, and then calculated on the halved copy of the number
of hits. Likewise, in the above picture, the minimum number of hits is 6,
and the minimum of the halved number of hits is 3, while the maximum is 116, and
maximum of the halved numbers is 58.
The two major topics for this tutorial are
Passing an array to a function, and
Creating a copy of an array within a function, and
returning that copy back to the calling function
A minor change to the game is that the blocks are now
initialized so that they start with -1 hits (instead of zero hits).
2. Examining The Program: Passing An Array To A Function (As A Parameter)
Let's examine the
C# source code that produces the behavior we see on-screen, starting with the
code that's specific to passing arrays as parameters (to functions).
Declaring the instance variables and named constants, and initialize
them
protected
override void
InitializeWorld()
{
// allocate memory for number of hits per each block
m_BlockHits =
new int[ARRAY_SIZE];
InitializeBlockHits(m_BlockHits);
#region
Allocate memory and initialize the block array
m_Blocks =
new
XNACS1Rectangle[m_BlockHits.Length];
For this tutorial, we don't need to declare any new instance variables,
nor any new named constants. However, we have added a new function to
help with the initialization of the array, named
InitializeBlockHits. Since C# will fill
the array with zeros for us, we will pick an arbitrary number with which to
initialize all the elements of the array: we've chosen -1 for this tutorial.
What's new is that we're now passing in a reference to the
m_BlockHits array, as a parameter,
to each of this new function:
InitializeBlockHits(m_BlockHits);
You'll notice that we're passing in a reference to the
m_BlockHits array, even though we
didn't use the ref
keyword. This is because arrays are always passed by reference.
(Yes, you can by a reference to an array by reference, using the
ref keyword on your
array parameter. The semantics of this are beyond this tutorial, and
should probably be avoided until you fully understand how this actually
works)
As was mentioned previously, arrays are passed by reference, even though
we didn't use the ref
keyword anywhere. What this means is that if you modify the elements
of the array, those changes will be visible to the calling function.
Contrast this with 'simple' types, such as integers, where you could
modify the parameter and not have to worry that some other part of the
program would see the new, changed value that you'd assigned to the
variable.
We're taking advantage of the fact that arrays are passed by reference
for our initialization function - we pass the array (by reference) to
InitializeBlockHits, which then changes all the values in the array.
The rest of the program then sees these new values, since
InitializeBlockHits was given a references to the same array that the rest
of the program uses.
InitializeBlockHits
///
<summary>
///
Set each element of the provided array to a starting value.
///
Right now, that starting value is -1, although you could change it.
///
</summary>
///
<param name="intArray">The
array to initialize</param>
private
void InitializeBlockHits(int[]
intArray)
{
// initialize each hits to be -1
for (int
i = 0; i < intArray.Length; i++)
intArray[i] = -1;
}
The first think you'll notice is that the function has a parameter, and
that the parameter is an array:
private
void
InitializeBlockHits(int[] intArray)
Notice that syntax-wise, passing an array is just like passing any other
type of variable. You list the type (int[]),
then the name (intArray).
Notice that in the calling function (InitializeWorld, in this case), we
call the function like normal, and list the name of the array:
InitializeBlockHits(m_BlockHits);
When passing an array as a parameter, we do NOT use brackets at all.
We want to pass the ENTIRE ARRAY, not just a single element.
Once the function has begun, we can see that it's using the normal
'traverse the entire array' pattern to iterate through all elements in the
array. For each element, we assign it the value -1:
for
(int
i = 0; i < intArray.Length; i++)
intArray[i] = -1;
Notice that because we're using the .Length property of the array, we
don't need to pass any extra parameters into the function in order to know
what the length of the array is.
UpdateWorld():
#region
Similar to previous example, only difference: array stat functions
take parameters
//
collect stats of hits by calling the new array stat functions
int
total = SumOfArray(m_BlockHits);
//
this was: TotalHits();
float avg =
AverageOfArray(m_BlockHits);
// this was:
AverageHits();
int min =
MinInArray(m_BlockHits);
//
this was: MinHits();
int max =
MaxInArray(m_BlockHits);
//
this was: MaxHits();
#endregion
UpdateWorld for this tutorial is almost identical to the UpdateWorld for
the one for the previous tutorial, down to the name of the temporary, local
variables that we're using here.
Each of the four methods have been similarly modified, so let's examine
the first one in detail.
SumOfArray():
///
<summary>
///
Compute total number of hits by adding up the contents
///
of all intArray content.
///
</summary>
///
<param name="intArray">an
integer array</param>
///
<returns>Sum
of all elements in intArray</returns>
private
int SumOfArray(int[]
intArray)
{
int total = 0;
for
(int
i = 0; i < intArray.Length; i++)
total = total + intArray[i];
return total;
}
Above is the code for the SumOfArray method, which sums up all the total
numbers in the intArray array. This is almost identical
to the TotalHits method from the previous tutorial. The only
substantive change is that the method is now given an array to work as a
parameter, rather than only working on one, specific array identified by an
instance variable.
For 'Phase 2', we use a loop to iterate over every element in the array:
for
(int
i = 0; i < intArray.Length; i++)
total = total + intArray[i];
You'll notice that because we're using the provided parameter, our code
access whichever array intArray refers to (instead of always using
the m_BlockHits array)
AverageOfArray, MinInArray, and MaxInArray have been similarly modified.
3. Examining The Program: Returning An Array From A Function
Let's examine the
C# source code that produces the behavior we see on-screen, starting with the
code that's specific to creating a copy of the hits array, and returning it.
You'll notice that when the program runs, the game displays (essentially) two
sets of numbers across the top. The second set of numbers is calculated
using a copy of the the m_BlockHits array, except that in the copy, all the
numbers are halved. We accomplish this by creating another array that is a
copy of the m_BlockHits array, going through, and halving the value of each
element in the array.
You will also notice that because our four functions (SumOfArray,
AverageOfArray, MinInArray, and MaxInArray) all accept an array as a parameter,
this means that we can reuse those functions again here. This is much
better than creating a
duplicate set of nearly-identical functions that do almost the same thing except on a
different array.
UpdateWorld():
#region make a
copy of m_BlockHits array, and compute the stats for the new array
int[]
newArray = CopyArray(m_BlockHits);
//
Just for fun, half all the values in the newArray
for
(i = 0; i < ARRAY_SIZE; i++)
newArray[i] = newArray[i] / 2;
// notice this is integer division
//
now: compute the stats. for the new array
int
newTotal = SumOfArray(newArray);
float newAvg =
AverageOfArray(newArray);
int
newMin = MinInArray(newArray);
int
newMax = MaxInArray(newArray);
#endregion
This portion of UpdateWorld starts by calling the new CopyArray
function, which we'll see in more detail shortly.
int[]
newArray = CopyArray(m_BlockHits);
You'll notice that this function both takes an array parameter (m_BlockHits),
AND it returns a new array. In order to use that new array, we need to
keep track of it using a variable. In this case, we keep track of the
new array using the local variable newArray.
CopyArray will return an exact duplicate of the array that we gave it -
the copy has the same number of elements, and each element has the same
value as the original. In order to make it clear that we are, in fact,
dealing with a different array, we choose to go through and halve each
element of the array:
// Just for fun, half all
the values in the newArray
for
(i = 0; i < ARRAY_SIZE; i++)
newArray[i] = newArray[i] / 2;
// notice
this is integer division
After that, the program then calculates the sum and average of the new
values in the array, as well as the finding the minimum and maximum values
in the new array, using the exact same functions as we used for the original
array. This is possible because the functions are told which array to
operate on via the parameter that we pass to it. In this case, we're
passing newArray, instead of m_BlockHits.
After this, UpdateWorld finishes by displaying the computed values for
the original array, and the halved array at the top of the screen.
(This code is not included above)
CopyArray():
///
<summary>
///
creates a new array, copy from srcArray and return the new array
///
</summary>
///
<param name="srcArray">Input
source array to be copied.</param>
///
<param name="size">Size
of the input array.</param>
///
<returns>A
copy identical to srcArray</returns>
private
int[] CopyArray(int[]
srcArray)
{
int[]
dstArray = new
int[srcArray.Length];
for
(int
i = 0; i < srcArray.Length; i++)
dstArray[i] = srcArray[i];
return
dstArray;
}
Using the 'Three Phase' description explained in the previous tutorial,
we can see how CopyArray works. For the purposes of this example,
let's assume that when CopyArray starts, srcArray contains the following
values:
Variable Name
Value
srcArray
an array
with these values:
Index
0
1
2
3
4
Value
11
107
18
11
116
In 'Phase 1', CopyArray creates a new array, whose length is the same as
the "source array" (srcArray) that was passed in as a parameter.
We assign the reference to that new array to the variable dstArray
(for "destination array")
Because C# automatically puts zeros into each element of the brand-new
array, this line will produce the following result:
Variable Name
Value
srcArray
an array
with these values:
Index
0
1
2
3
4
Value
11
107
18
11
116
dstArray
an array
with these values:
Index
0
1
2
3
4
Value
0
0
0
0
0
This is somewhat close to what we want (the arrays both contain
integers, and are the same length), but dstArray isn't an exact copy of
srcArray (the elements of dstArray do not contain the same values as the
corresponding elements of the srcArray).
In 'Phase 2', CopyArray iterates through both arrays, copying numbers
from the source array (srcArray) to the destination array (dstArray).
Let's trace a couple iterations to see how this works out:
In the first iteration of the loop, i is assigned the value
zero, which is less than the length of the source array. As a
result, the line
dstArray[i] = srcArray[i];
is executed, which copies the
11 in srcArray[0] into element 0 of dstArray. The
following table shows the values of the various variables, BEFORE the i++
has been executed:
Variable Name
Value
srcArray
an array
with these values:
Index
0
1
2
3
4
Value
11
107
18
11
116
dstArray
an array
with these values:
Index
0
1
2
3
4
Value
11
0
0
0
0
i
0
You'll notice that at this point, the green 'Already Done' region of the
arrays consists of just element 0. In the next iteration, the blue
'Current Element' region will be element #1 of both arrays, with the
'Future Work' region consisting of elements #2 and higher.
In the next iteration of the loop, i is incremented to
the value of 1, which is less than the length of the source array.
As a result, the line
dstArray[i] = srcArray[i];
is executed, which copies the
107 in srcArray[1] into element 1 of dstArray. The
following table shows the values of the various variables, BEFORE the i++
has been executed:
Variable Name
Value
srcArray
an array
with these values:
Index
0
1
2
3
4
Value
11
107
18
11
116
dstArray
an array
with these values:
Index
0
1
2
3
4
Value
11
107
0
0
0
i
1
You'll notice that at this point, the green 'Already Done' region of the
arrays consists of elements #0 and #1. In the next iteration, the
blue 'Current Element' region will be element #2 of both arrays, with the
'Future Work' region consisting of elements #3 and higher.
In the next iteration of the loop, i is incremented to
the value of 2, which is less than the length of the source array.
As a result, the line
dstArray[i] = srcArray[i];
is executed, which copies the
18 in srcArray[2] into element 2 of dstArray. The
following table shows the values of the various variables, BEFORE the i++
has been executed:
Variable Name
Value
srcArray
an array
with these values:
Index
0
1
2
3
4
Value
11
107
18
11
116
dstArray
an array
with these values:
Index
0
1
2
3
4
Value
11
107
18
0
0
i
1
You'll notice that at this point, the green 'Already Done' region of the
arrays consists of elements #0, #1 and #2. In the next iteration,
the blue 'Current Element' region will be element #3 of both arrays, with
the 'Future Work' region consisting of elements #4 and higher.
The CopyArray continues to follow this pattern of execution until all
values have been copied from srcArray into dstArray.
When the loop has finished, all values have been copied from srcArray to
dstArray, and so for this function, there really isn't a 'Phase 3'.
After this, the only thing left is to send the new array back to the calling
function:
return
dstArray;
Once again we use the name of the array, but NOT any brackets - we want
to send the whole array back, not just a single element.
FURTHER EXERCISES:
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 referring to 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.
Examining The AvergeOfArray()
Function
For this exercise, you should use the same project that was explained in the
above tutorial.
Notice that the numbers stored in the
intArray array are integers, and that
SumOfArray returns an integer.
Notice also that we are specifically declaring
total to be a
float, not an
int.
For the version of the function (which is subtly different from the
AverageHits in the previous tutorial), is it strictly necessary to declare
total to be a
float?
Examining The CopyArray()
Function
For this exercise, you should use the same project that was explained in the
above tutorial.
In UpdateWorld, when the program first calculates the sum, average, etc on
the original array, and then goes back and calculates these values for the
new, 'halved' array. Why are all the values in the 'halved'
array half of what they are in the original, rounded down?
Creating a IncrementAll function
For this exercise, you should use the same project that was explained in the
above tutorial.
For this exercise, you should create a function named IncrementAll,
which will be given an integer array as a parameter, and will go through and
increase the value of each element by one. Try modifying your game so
that UpdateWorld calls this function.