XNA 101 .Net

Learn how to program in C# while writing games in XNA Game Studio 3.0!
Our upcoming Silverlight book for beginners (includes a great chapter on game development in Silverlight!) Hello! Silverlight 2 with Dave Campbell, available online now!



Pages

Recent posts

Navigation

Archive

Blogroll

    Tampa Divorce Lawyer

    North of Tampa in Lutz, Florida. A Tampa Divorce Lawyer focusing on family, divorce, and real estate law.

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

    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(); 
       balls[index].Velocity = 
           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) 
    { 
       ball.Update(); 
    } 

    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) 
    { 
       ball.Draw(spriteBatch); 
    } 

    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.

    50bals

    Posted: Nov 14 2008, 11:07 by Bill Reiss | Comments (4) RSS comment feed |
    • Currently 2.833333/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under:

    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); 

    to:

    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.

    Posted: Nov 14 2008, 10:52 by Bill Reiss | Comments (3) RSS comment feed |
    • Currently 5/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under:

    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:

    http://www.flashkit.com/downloads/soundfx/wav/7730/Boing.zip

    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:

    using Microsoft.Xna.Framework.Audio;  

    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:

    bool bounced = false;

    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:

    bounced = true;

    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) 
       2: { 
       3:    Boing.Play(); 
       4: } 

    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.

    Posted: Nov 11 2008, 13:27 by Bill Reiss | Comments (8) RSS comment feed |
    • Currently 5/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under:

    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.

    trans

    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:

    shadow

    Posted: Nov 11 2008, 04:59 by Bill Reiss | Comments (2) RSS comment feed |
    • Currently 5/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under:

    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 :)

    xna101_background

    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:

    Texture2D background; 

    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.

    Rectangle backgroundRectangle; 

    and in the Game1.LoadGraphicsContent() method, after the content.Load() calls, add the following:

    backgroundRectangle = new Rectangle(0, 0, 
        graphics.GraphicsDevice.Viewport.Width, 
        graphics.GraphicsDevice.Viewport.Height);

    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.

    spritebackground

    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.

    Posted: Nov 09 2008, 12:32 by Bill Reiss | Comments (7) RSS comment feed |
    • Currently 5/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under:

    XNA 3.0 Released!

    Well it’s here even before I thought it would be, download XNA 3.0 now:

    http://www.microsoft.com/downloads/details.aspx?FamilyId=7D70D6ED-1EDD-4852-9883-9A33C0AD8FEE&displaylang=en

    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.

    Posted: Nov 01 2008, 13:42 by Bill Reiss | Comments (0) RSS comment feed |
    • Currently 5/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under:

    Lesson 10: Sprite Color

    Source code for this lesson completed: http://www.bluerosegames.com/xna101/lessons/XnaLesson10.zip

    So far, when drawing the sprites we've been using Color.White as the color to draw with. Color.White will preserve the original color of the sprite, but we can get some interesting effects by using other colors.

    In order to allow each BouncingBall sprite to have its own drawing color, let's add a DrawingColor Property to the BouncingBall class and an associated private field _drawingColor to store the color. The code will look something like this:

    private Color _drawingColor; 
     
    public Color DrawingColor
    {
       get { return _drawingColor; }
       set { _drawingColor = value; }
    } 
     

    In the BouncingBall constructor, add the following line to initialize the value in _drawingColor:

    _drawingColor = Color.White;

    So if we don't set the drawing color to something else, it will default to white.

    Now change the BouncingBall.Draw() method to use this color instead of Color.White. The method will now look like this:

    public void Draw(SpriteBatch spriteBatch)
    {
       spriteBatch.Draw(Texture, _position, _drawingColor);
    } 

    So again the Game1 class is simplified and more logic is put in the BouncingBall class. Now imagine that we have another sprite class that does something completely different than the BouncingBall class. We could still have the same Draw() and Update() methods, and call them exactly the same way from the Game1 class, and do two totally different things.

    So now if you run the program again, it does the same thing as always. Now for something completely different. Add the following image to your project by saving it to your computer and then right click on the Content folder in the solution and select "Add Existing Item" from the menu. It should be called white_ball.png.

    In the Game1.LoadContent() method,  change red_ball to white_ball. Then after the ball2.Velocity = new Vector2(10f, 5f);
    statement, add the following:

    ball2.DrawingColor = Color.Purple;

    Now if you run the program again, you should have a white ball and a purple ball.

    purple

    Colors can also be specified as a combination of Red, Green and Blue. Pretty much any color can be represented by the correct combination of red, green, and blue. So you can change the above line to:

    ball2.DrawingColor = new Color(255, 0, 255);

    Where the first argument is the amount of red the color will have, with 255 being the maximum and 0 being the minimum. The second argument is the amount of green for the color, and the last is the amount of blue. So this color will be full red, no green, and full blue. When referring to colors in computer programming, especially in web design, we often refer to the RGB value of a color (short for Red, Green Blue). Run the program again. The ball should now be a bright purple.

    Posted: Oct 18 2008, 12:45 by Bill Reiss | Comments (9) RSS comment feed |
    • Currently 3.166667/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under:

    Lesson 9: Using Properties

    Lesson 9 starts with the code as it was at the end of Lesson 8.

    Source code after the Lesson 9 changes: http://www.bluerosegames.com/xna101/lessons/XnaLesson9.zip 

    C# provides a nice alternative to accessing Fields directly from outside the class using something called Properties which are more like a method call, but look to other objects like public Fields.

    The benefits of Properties over direct access to Fields include but are not limited to:

    • Sanity checks, the object can check if a value is valid before it accepts the change. 
    • Ability to combine Field values to make things easier for the calling object.
    • The underlying structure of the data can be changed without requiring changes to the calling object.
    • Properties can be made read only or write only.
    • Easier to accommodate future needs of the class.

    There are also some negatives:

    • Fields inside value type properties are read only. You have to create a new copy of the entire value type (like Vector2) to set a new value. With fields, you can just say Position.X = 10, with properties you would have to do Position = new Vector2(10, Position.Y);
    • Properties cause a small performance hit. a bit more code has to run compared to accessing/setting a field.
    • They can make your code a bit more verbose impacting readability. 

    There are also some more advanced benefits having to do with packaging a class library as an assembly and maintaining compatibility across versions.

    For game programming, the use of properties can be a hotly debated topic, but Shawn Hargreaves who I highly respect falls on the side of using properties only when necessary. So consider this more of a "how to use properties" than a "you should always do this".

    In the case of the BouncingBall class, there are 2 public Fields that we will convert to Properties. Here is a sample property definition which can replace the previously public Field Position:

    private Vector2 _position;
     
    public Vector2 Position
    {
        get
        {
            return _position;
        }
        set
        {
            _position = value;
        }
    }

    Notice the "get" and "set" blocks. If you remove the "set" block, the property would be read only. Likewise, if you remove the "get" block, the property would be write only. Since _position is declared private, other objects will not be able to access the value of _position directly, only through the property Position. Go ahead and replace the declaration of Velocity as follows:

    private Vector2 _velocity;
     
    public Vector2 Velocity
    {
        get
        {
            return _velocity;
        }
        set
        {
            _velocity = value;
        }
    }

    In the "set" block, notice that there is a reference to a field named value. This is a special keyword that is used when setting a property and always contains the value that is being assigned to the property. It has the same data type as the property definition, or in this case, Vector2. So in the case of the BouncingBall class, if we had the following line in our Game1 class:

    ball2.Velocity = new Vector2(10, 5); 

    then in the Property setter for Velocity, "value" would contain a Vector2 object with X of 10 and Y of 5.

    In most classes, the methods of the class will probably deal directly with the private Fields instead of going through the property logic, although in some cases it will make sense to access the data through the Properties as well, if some of the same reasons listed above apply to these values. In the BouncingBall class, it probably makes sense to access the Fields directly from inside the class, so in the Update method and the constructor, change all references to the properties to the Field names instead.

    So change the BouncingBall.Update() method to use the private Fields instead of the public Properties:

    public void Update()
    {
       // TODO: Add the BouncingBall update logic here
       _position = _position + _velocity;
       if (_position.X < 0 || _position.X > GraphicsViewport.Width - Texture.Width)
       {
          // If we get in here, we've hit a vertical wall
          _velocity.X = -_velocity.X;
          _position.X = _position.X + Velocity.X;
       }
       if (_position.Y < 0 || _position.Y > GraphicsViewport.Height - Texture.Height)
       {
          // If we get in here, we've hit a horizontal wall
          _velocity.Y = -_velocity.Y;
          _position.Y = _position.Y + _velocity.Y;
       }
    } 

    So run the program again, and it should behave exactly as it did before the changes.

    So it's really up to you and up to the situation. Use Properties if they help you with whatever you're trying to accomplish. In most cases, you can change a Field to a Property later if you need to.

    Posted: Oct 18 2008, 11:17 by Bill Reiss | Comments (2) RSS comment feed |
    • Currently 5/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under:

    Lesson 8: Double Trouble (Part 2)

    In this lesson we continue creating the BouncingBall class by moving the BouncingBall related logic into the class.

    This lesson picks up right where lesson 7 left off.

    Source code after completion of lesson 8: http://www.bluerosegames.com/xna101/lessons/XnaLesson8.zip

    In Part 1, we cleaned up our code a bit by creating a BouncingBall class and moving the BouncingBall related data into that class. The fancy term for this is encapsulating the data in a class. However, it usually isn't enough to just encapsulate the data, we also need to move the BouncingBall related logic into methods of the BouncingBall class. If you notice from Part 1, we cleaned up the declarations in the Game1 class (reducing 4 declarations to 1) but the Game1.Update() method actually got uglier.

    So in Part 2 we'll tackle the Update() method and clean it up.

    Following the model that XNA has implemented where XNA calls the Update method of the Game1 class to update positions, etc. we can do the same thing for the BouncingBall class. We will tell the BouncingBall objects to Update themselves.

    First define a new Update() method in the BouncingBall class. Inside the BouncingBall class block, add the following lines of code:

    public void Update()
    {
        // TODO: Add the BouncingBall update logic here
    }

    Then replace the // TODO line with all of the ball1 related code from the Game1.Update() method (so everything in the method except the first and last statements) and then remove the "ball1." prefix from everything in the new code. We won't need the "ball1." references any more because this code is now part of our BouncingBall class and it knows which copy of the data to access.

    Now the BouncingBall.Update() method should look like this:

    public void Update()
    {
       // TODO: Add the BouncingBall update logic here
       Position = Position + Velocity;
       if (Position.X < 0 || Position.X > graphics.GraphicsDevice.Viewport.Width - spriteTexture.Width)
       {
          // If we get in here, we've hit a vertical wall
          Velocity.X = -Velocity.X;
          Position.X = Position.X + Velocity.X;
       }
       if (Position.Y < 0 || Position.Y > graphics.GraphicsDevice.Viewport.Height - spriteTexture.Height)
       {
          // If we get in here, we've hit a horizontal wall
          Velocity.Y = -Velocity.Y;
          Position.Y = Position.Y + Velocity.Y;
       }
    } 

    Uh oh, there's a couple of problems. The spriteTexture object is declared in the Game1 object, and we can't access it from inside the BouncingBall class directly. There are a few ways to handle this, but in this case let's create static Fields in the BouncingBall class to store the sprite texture and the game Viewport. Static Fields are Fields that can be accessed by all objects of that type and can be accessed from outside the class by specifying the class name instead of an instance of the class. So after the "public Vector2 Velocity;" declaration at the top of the BouncingBall class definition, add the following:

    public static Texture2D Texture;
    public static Viewport GraphicsViewport; 

    In the BouncingBall.Update() method, change all references to spriteTexture to Texture, and graphics.GraphicsDevice.Viewport to GraphicsViewport. So now the BouncingBall.Update() method should look like this:

    public void Update()
    {
       // TODO: Add the BouncingBall update logic here
       Position = Position + Velocity;
       if (Position.X < 0 || Position.X > GraphicsViewport.Width - Texture.Width)
       {
          // If we get in here, we've hit a vertical wall
          Velocity.X = -Velocity.X;
          Position.X = Position.X + Velocity.X;
       }
       if (Position.Y < 0 || Position.Y > GraphicsViewport.Height - Texture.Height)
       {
          // If we get in here, we've hit a horizontal wall
          Velocity.Y = -Velocity.Y;
          Position.Y = Position.Y + Velocity.Y;
       }
    } 

    Now in the Game1 class, remove the spriteTexture definition from the class, since it will be stored in the static Texture Field in the BouncingBall class. Change the Game1.LoadContent() method to look like this:

    protected override void LoadContent()
    {
        // Create a new SpriteBatch, which can be used to draw textures.
        spriteBatch = new SpriteBatch(GraphicsDevice);
     
        // TODO: use this.Content to load your game content here
        BouncingBall.Texture = Content.Load<Texture2D>("red_ball");
        BouncingBall.GraphicsViewport = graphics.GraphicsDevice.Viewport;
     
    }

    Note the use of the BouncingBall class name to set the static fields.

    Inside the Game1.Update() method, we need to replace the code to update the ball's position with a call to the ball1 Update() method. So Game1.Update() should now look like this:

    protected override void Update(GameTime gameTime)
    {
       // Allows the default game to exit on Xbox 360 and Windows
       if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
          this.Exit();
       ball1.Update();
       base.Update(gameTime);
    } 

    Now add a BouncingBall.Draw() method as follows:

    public void Draw(SpriteBatch spriteBatch)
    {
       spriteBatch.Draw(Texture, Position, Color.White);
    } 

    and in the Game1.Draw() method, change the spriteBatch.Draw() statement to this:

    ball1.Draw(spriteBatch);

    This is MUCH cleaner. Notice how little code there is now in the Game1 class. It makes the code easier to understand, and it will also be easier to manitain your code and to make changes later. Run the program and see that it still behaves the way it was behaving before.

    Now for the second ball. EVERYWHERE in the Game1.cs file where there is a reference to ball1, copy the line and paste another line directly below the first, changing all "ball1"s to "ball2"s.

    Run the program again. What? No difference? This is because both balls are being drawn in exactly the same locations. They have the same x and y positions and velocities. So after we create the ball2 object, let's change some of its data. At the bottom of the Game1.Initialize() method, after the line

    ball2 = new BouncingBall(); 

    add the following:

    ball2.Velocity = new Vector2(10f, 5f); 

    Now the initial x velocity of ball2 will be 10, and the initial y velocity will be 5. Run the program again and you should see 2 balls bouncing around at different speeds.

    I hope that now you can see the power of creating and using classes in your program. Basically any time you find yourself copying the same code you should ask yourself if there is some way you can create a class to simplify things.

    Posted: Oct 17 2008, 05:05 by Bill Reiss | Comments (2) RSS comment feed |
    • Currently 5/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under:

    XNA 3.0 to be released on October 30

    October 30 isn't that far off so now I'm really glad I decided to go straight to 3.0 while updating these posts.

    http://creators.xna.com/en-us/xna_goes_live_october30th

    Very exciting, and I hope to have at least one game ready for the Xbox Community Games when it goes live.

    Posted: Oct 15 2008, 02:40 by Bill Reiss | Comments (0) RSS comment feed |
    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5
    Filed under: