Dr. Popper for Zune written with XNA 3.0
I'm pleased to announce that I have successfully got my Dr. Popper game running on Zune. Here is a screenshot:
If you're reading this blog, hopefully you already have XNA 3.0 installed. If not, you'have to install it to play this game.
More here: http://www.bluerosegames.com/xna101/page/Dr-Popper-for-Zune.aspx
Lesson 15: Arrays and Loops
This lesson introduces the concepts of arrays and loops and how their use can simplify your code, especially when dealing with larger numbers of objects.
This lesson picks up where Lesson 14 left off.
Source code for Lesson 15 Completed: http://www.bluerosegames.com/xna101/lessons/XnaLesson15.zip
When it came time to add a second ball to our game, it became clear that just copying and pasting the code and changing the variable names was going to be a problem. So by encapsulating the bouncing ball logic in a BouncingBall class, we were able to reduce dramatically the complexity and the lines of code of our game.
Now what if we want to have 3 balls, or 10, or 100? Should we declare 100 separate BouncingBall objects in the Game1 class initialize them in the Game1 constructor, and have 100 Update and Draw statements? Again, this would get out of control pretty quickly. Fortunately C# provides an alternative.
An array is a fixed size list of objects or primitives of the same data type. Items in the array are accesed via an index. An index is a value that "points" to an element in the array. Typically array indexes are integers. In C# array indexes start at 0, so the first element has an index of 0.
Often when writing code, you get to a point where the code in unmanageable and it's necessary to add classes, move code into new methods, or make other changes. The term refactoring refers to a restructuring of the code while preserving all or most of the current functionality. Ideally after a refactoring, the program should work exactly the same as it did before. In the "Double Trouble" lessons, we refactored the code by creating a BouncingBall class and moving the bouncing ball logic and data into that class. In this lesson, we will refactor the code to use an array to store the BouncingBall information.
The format for declaring an array is as follows:
datatype  fieldName;
Where the data type can be a primitive, such as float, or a class, such as BouncingBall. In our case, in the Game1 class, replace the ball1 and ball2 declarations in the Game1 class with the following:
BouncingBall  balls;
int ballCount = 2;
The ballCount Field will help us later when we want to change how many elements are in our array. This is a best practice, try not to use constant values all over your code because it makes changes harder later.
The next step for an array is to initialize the array. At the top of the Game1.Initialize() method, add the following:
balls = new BouncingBall[ballCount];
This initializes the array to a size of 2 (since ballCount is 2). Now that the array itself is initialized, we need to initialize each object in the array. We can, as we had before, have a single "new" statement for each object we want to create, but again this can be an issue if you have a lot of objects. So in order to reduce the amount of code, we can use something called a loop. A loop is a block of code that is run multiple times based on conditions. In this case, we will use a "for" loop. Typically a "for" loop is used when you want to start at one number and end at another number. Our loop will start at the smallest index in our array, and end at the largest index. Replace all of the rest of the the ball1 and ball2 code which initializes the objects and sets values in the Game1.Initialize() method with the following:
for (int index = 0; index < ballCount; index++)
balls[index] = new BouncingBall();
new Vector2(rand.Next(2, 11), rand.Next(2, 11));
byte r = (byte)rand.Next(64, 256); // Red value
byte g = (byte)rand.Next(64, 256); // Green value
byte b = (byte)rand.Next(64, 256); // Blue value
byte a = (byte)rand.Next(64, 256); // Alpha value
balls[index].DrawingColor = new Color(r, g, b, a);
The structure of a for statement has 3 semicolon delimited satements inside the parentheses. The first is executed the first time through the loop and typically initializes the loop index. The second statement is a condition that once false, program execution continues with the next statement after the loop block. The condition is checked every time through the loop, including the first time. If the condition is false on the first time through the loop, the code inside the loop block will not get executed at all. The third statement updates the loop index and is executed at the end of each time through the loop, before the condition is checked again.
Notice the index++ syntax. The "++" operator is a more efficient way of adding 1 to the value in the variable. It is very common in C# and C++ programming (which incidentally is how C++ got its name) and is the preferred way to increment a variable. There is a corresponding "--" operator which subtracts 1 from a variable.
So this loop set index to 0, adding 1 each time through the loop, and will execute as long as index is less than 2. Why less than 2? Because as stated above, arrays in C# start at 0, so an array with 2 elements in it has indexes of 0 and 1. Again this is a boundary condition and as discussed in a previous lesson, you need to be very careful around boundary conditions and it's very easy to get them wrong if you're not careful.
Another type of loop is the "foreach" loop. The "foreach" loop is specifically for looping through an array or list of objects. Though not as flexible as a "for" loop, it is easier to use. For our Update and Draw method changes, let's use the "foreach" loop. First in the Game1.Update() method, remove the ball1 and ball2 Update calls and replace them with the following:
foreach (BouncingBall ball in balls)
As you can see, you don't have to access the array element by index, or do the initialization of the loop variable or checking a condition, that's all handled for you. Similarly in the Game1.Draw() method, replace the ball1 and ball2 calls with this:
foreach (BouncingBall ball in balls)
Run the program again. You should see 2 balls of random color, opacity, and velocity bouncing around. Run it again, and you will see other combinations of colors and velocities.
Now for the fun part. Change the ballCount initializer to set ballCount to 10. Run it again. Now set it to 50. As you can see, arrays and loops are very powerful and a key concept in programming.
Lesson 14: Random Thoughts
Random numbers are very important to game development. This lesson introduces how to use random numbers in your program.
As usual, we start where the last lesson left off.
Complete source code for lesson 14: http://www.bluerosegames.com/xna101/lessons/XnaLesson14.zip
In most games, the way the game behaves depends on a few factors. Of course one of these is the input from the user who is playing the game via the keyboard, mouse, joystick or game pad. Another is some standard patterns that enemies or other game elements follow based on the situation. However if a game only relied on these factors, the game would get boring quickly since it would always behave in a similar way. A third factor to how a game behaves is randomness. Whether it's the shape of the next piece in a tetris game, or the little unpredictable behaviors of a boss at the end of a Super Mario level, randomness is an important factor in most games.
The .Net Framework provides a class to C# which generates pseudo random numbers. These numbers are not true random numbers, since the numbers still follow a pattern, but the pattern is so huge and varied that the numbers appear to be random and are uniformly distributed across a range if run through enough iterations. This class, appropriately, is the Random class.
In our MyFirstGame program, let's use the Random class to control the initial velocity of the bouncing balls.
First we need to create an object of type Random. Let's do this in the Game1 class, so that it is available to all members of the Game1 class. We don't want to create an instance of the Random class each time we need a random number, both for speed reasons and it could impact how random our numbers are.
So at the top of the Game1 class definition block with the rest of our Game1 Field definitions, add the following:
Random rand = new Random();
Now this statement is a bit different than what we've done in the past. As an alternative to instantiating the Field in the constructor, you can do what is called Field Initialization. In C#, Field Initialization occurs before the constructor is executed. It is simpler to initialize an object this way if certain conditions are met:
- The Field's constructor must not require any data from any other Fields or any non-constant data.
- The Field's constructor must not be able to throw an error. This is because there is no way to handle that error properly. If the constructor can throw an error, it is better to instantiate the object in the constructor.
One of the key parts of the Random class is what is referred to as a seed value. If you initialize a Random object with the same seed value, you will get the same sequence of random numbers every time. If a seed value is not passed in to Random's constructor, the seed value is generated from the system clock, giving a seed value that is going to be different each time unless 2 Random objects are created in the same clock cycle. This can happen on high performance equipment, but since we are only creating one Random object we don't really need to worry about this.
So now to generate the random numbers. The Random.Next() method has 3 overloads, or 3 different sets of arguments that it can accept. Overloaded methods have the same method name but have different arguments that are passed into the method. They must differ by data type, number of arguments, or both.
- If Random.Next() is called with no arguments, the result is a non-negative random number greater than or equal to zero, and less than 2,147,483,647.
- If one argument is passed in, the result is a non-negative random number greater than or equal to 0, and less than the value in the argument.
- If two arguments are passed in, the result is a random number greater than or equal to the first number, and less than the second.
Note that for the Random.Next() methods, in the case of the lower bound, or the minimum value returned, the call is inclusive, meaning the value returned can be equal to the lower bound, and in the case of the upper bound, the call is exclusive. The number returned will always be less than, but not equal to, the upper bound. When dealing with ranges, I will often refer to the lower bound and the upper bound. The values equal to and near (slightly larger or smaller than) the lower and upper bounds are called boundary conditions, and are the source of a large percentage of bugs in programs. When building test cases for applications, special attention should be placed on creating test cases for all of the boundary conditions.
In this particular case, we will use the third overload of Random.Next() with a minimum value of 2, and a maximum value of 11, which will generate numbers between 2 and 10.
In the Game1.Initialize() method, change the following statement:
ball2.Velocity = new Vector2(10f, 5f);
ball1.Velocity = new Vector2(rand.Next(2, 11), rand.Next(2, 11));
ball2.Velocity = new Vector2(rand.Next(2, 11), rand.Next(2, 11));
As part of each call to rand.Next(), the seed is updated automatically inside the rand.Next() call so that the next value generated (on the next call to rand.Next()) will be based on that seed. Run the program again a few times and see that each time the balls move at different speeds.
Lesson 13: The Delicate Sound of Thunder
Sounds are something that got a lot easier in XNA 3.0. You used to have to create an XACT project, add your sounds to that, and then add your XACT project to your XNA project. Then there were a couple of hoops you had to jump through in your code to make it work. You can still do sounds the old way, and in some cases especially when doing sound mixing, you may need to, but for simple sound effects the technique shown in the sample is quick and easy.
This lesson begins where Lesson 12 leaves off.
Lesson 13 Completed Source Code: http://www.bluerosegames.com/xna101/lessons/XnaLesson13.zip
Sound effects are so important to game development. You can do a great sprite animation for an explosion, but without the KABOOM! it loses a lot of its impact.
For this sample, we?re going to use a free sound from Flashkit.com, a great resource for free sounds. Another great place is A1 Free Sound Effects: http://www.a1freesoundeffects.com/noflash.htm
From Flashkit.com, download the following:
Extract the sound file Boing-Sith_Mas-479.wav and add it to your Content folder.
XNA 3.0 provides the SoundEffect class to make it easy to play sound effects. This class is located in the Microsoft.Xna.Framework.Audio namespace, so make sure your BouncingBall.cs and Game1.cs files have the following using statement at the top:
Let?s declare a static field in the BouncingBall class to hold the sound effect:
static public SoundEffect Boing;
Then like we did previously for textures, we?ll do a Content.Load in the Game1.LoadContent method:
BouncingBall.Boing = Content.Load<SoundEffect>("Boing-Sith_Mas-479");
Now that the sound effect is loaded, it?s time to figure out when to play the sound. In the BouncingBall.Update() method, add this declaration to the beginning of the method:
This declares a boolean, or true and false value, that we can use as a flag to determine whether a bounce took place. As part of the declaration, we are setting its initial value to false. It will keep that value until the end of the method unless it is changed.
So now inside the both of the two conditional "if" blocks, add the following statement:
and lastly, at the end of the method, if bounced is true, meaning we hit a wall, we want to play the sound. So add the following lines:
1: if (bounced == true)
In C#, the double equals sign in the "if" conditional statement means we are doing a comparison, not an assignment. If we used a single equals sign there, the code would think we wanted to set bounced to true instead of checking to see if it was true, and so the conditional would always be true, and the sound would always play.
That?s all there is to it. Run the program again. If all went well, you should get a boing sound every time a ball hits a wall.
Lesson 12: Transparent Sprites
This lesson focuses on how to draw a sprite in XNA where some of the background or sprites behind it shows through.
We pick up where Lesson 11 left off.
Source code for Lesson 12 completed: http://www.bluerosegames.com/xna101/lessons/XnaLesson12.zip
When I first started digging into XNA, I had a hard time figuring out how to draw a sprite so that some of the background shows through. Some reasons you may want to draw a sprite that you can see through is for fog effects, making a character glow, or for a drop shadow on an object. It turns out that it's very simple, and that could be one of the reasons I had trouble finding information on the topic.
When we draw the sprite, we can draw it with less than 100% opacity. 100% opacity means the object cannot be seen through whatsoever, and 0% opacity means the object is completely transparent.
The Color object that we used in lesson 10 to tint the sprite can also be used to control the opacity. The Color object has a fourth parameter besides Red, Green, and Blue. This parameter is called Alpha and controls the amount of opacity to draw with. This value, like the Red, Green, and Blue values has a range of 0 to 255, with 255 being completely opaque, and 0 being completely transparent.
So all we have to do to is pass a color to the Draw() method which specifies a value less than 255 for the Alpha value. The Color object has another constructor which takes four arguments with the fourth being the Alpha value.
In the Game1.Initialize() method, change the statement that sets ball2's drawing color to the following:
ball2.DrawingColor = new Color(255, 0, 255, 128);
128 is halfway between 0 and 255, so ball2 will be drawn at 50% opacity.
Run the program again and see that the background now shows through the purple ball.
In the game I'm currently working on, I use transparency to draw a drop shadow. This is fairly straightforward to add to the BouncingBall class. To draw a drop shadow, draw the same texture as the sprite itself, but offset, with a black tint, and with a lower opacity value than the sprite itself. To do this, let's add a new private Color field to the BouncingBall class called _dropShadowColor. In this case, we won't expose a public property to set the _dropShadowColor, instead it will be set inside the class.
Add the following line to the BouncingBall class to declare _dropShadowColor
private Color _dropShadowColor;
Then in the constructor, let's give it a default value.
_dropShadowColor = new Color(0, 0, 0, 128);
The drop shadow color, by default, will be black with 50% opacity.
However, if we set the drawing color to something other than 100% opacity, we need to adjust the drop shadow opacity accordingly. So in the BouncingBall.DrawingColor Property's set method, add the following:
_dropShadowColor = new Color(0, 0, 0, (byte)(_drawingColor.A / 2));
"A" is the Alpha value of the color, so this sets the Alpha of the drop shadow to half of the value of the drawing color's Alpha value. The "/" character performs division, and the "(byte)" casts the value into a value of type byte. The byte data type is what the constructor expects to be passed in for the Alpha value, and has a value of 0 to 255. When one data type is converted to another it is called casting. The division causes an implicit cast to type int, so it needs to be cast back to a byte or you'll get an error when you build the program. More about casting in a later lesson.
So this is a good example of how properties can be useful. Just by setting the DrawingColor property, we actually set the value of two fields.
So all that's left is to Draw the drop shadow. It needs to be drawn before the actual sprite, for the same reason we had to draw the background first. So at the beginning of the BouncingBall.Draw() method add the following:
spriteBatch.Draw(Texture, _position +
new Vector2(10f, 10f), _dropShadowColor);
which will draw the drop shadow using the drop shadow color at a position 20 pixels to the right and below the sprite. Run the program again and it should look like this:
Lesson 11: Sprite Scaling
This Lesson picks up where Lesson 10 left off.
Source code after completing this lesson: http://www.bluerosegames.com/xna101/lessons/XnaLesson11.zip
In all of the lessons so far, when drawing sprites, we're drawing them at the same scale as the original image. There are many cases where this is not desirable. Let's say you want it to look like one object is farther way than another, or you want to do some animation effects that involve an object getting smaller or larger. Textures use a lot of system resources, and it's not desirable to have to have the same texture loaded at a few different sizes in order to produce this effect.
Or maybe you want to draw a background image for your game and stretch it to fit the window. This is the example we'll focus on for this lesson.
First we need to add the background image to the solution. Save the image below as xna101_background.png or create your own and add it to your solution in the Content folder. Please forgive the free advertising message :)
The background image, even though it takes up the whole game surface, is still a sprite, and will be loaded and drawn exactly like our other sprites, except for a slight change to scale the sprite to fit the screen. So add a declaration for the background Texture2D object immediately after the declaration for the ball2 object in the Game1 class. The declaration will look like this:
Now we need to load the image file into the Texture2D object. To do this, add the following line after the other Content.Load() call in Game1.LoadContent():
background = Content.Load<Texture2D>("xna101_background");
Now to draw it. The background needs to be drawn first, or you won't be able to see the other sprites. In order to scale the sprite as we draw it, we need to use a different version of the SpriteBatch.Draw method, which instead of taking a Vector which specifies the X and Y location of the destination's upper right corner, it takes a Rectangle object which specifies X, Y, Width and Height of the bounding rectangle of the destination sprite. By specifying the width and height, the SpriteBatch object knows to scale the sprite from its original size to the size specified. So in the Game1, declare a Rectangle object which will hold the destination information.
and in the Game1.LoadGraphicsContent() method, after the content.Load() calls, add the following:
backgroundRectangle = new Rectangle(0, 0,
Now in Draw() method, immediately after the spriteBatch.Begin() call, add the following:
spriteBatch.Draw(background, backgroundRectangle, Color.White);
Now run the program again. The background should be scaled to fit the game surface.
You'll notice that it's pretty blurry. This is a side effect of scaling the image to a size larger than the original. Typically when scaling sprites, I like to start with an image that is equal to or larger than the largest size it will have on the screen, and scale it smaller. This will make for a better looking image.
Note that the width and the height of the destination rectangle don't need to be proportional to the original image. You can stretch the image's proportions by any amount vertically or horizontally.
XNA 3.0 Released!
Well it’s here even before I thought it would be, download XNA 3.0 now:
Nothing had to change in the code so far between the Beta and Release, so everything should still just work after a recompile. I have updated all of the downloadable samples to the 3.0 release.