Sei sulla pagina 1di 7

XNA 4.

0 Break Out Tutorials Part 2 Bricks


I'm writing these tutorials for the new XNA 4.0 framework. The tutorials will make more sense if they are read in order. You can find the list of tutorials on the XNA 4.0 Tutorials page of my web site. I will be making my version of the project available for download at the end of each tutorial. It will be included on the page that links to the tutorials. This is a beginner tutorial on creating a Break Out style game. I will be writing tutorials in the future on creating the game using object-oriented programming principles. The tutorial is more to introduce XNA with a more or less complete game. It won't go into state management but it will be a functional brick breaking game. In this part I'm going to add in bricks to be broken. You are going to need an image for the bricks. Instead of having many bricks of different colors, I have one brick that I tint for different colors. You can download the brick from http://xnagpa.net/xna4/downloads/brick.zip. Download and extract the files to a directory. Load up your game from the previous tutorial. You need to add the brick to the content project. There is a way other than right clicking the content project to add game content. Open Windows Explorer and navigate to where you extracted the image. Size and position the Windows Explorer window so that you can see the solution explorer in Visual Studio. Drag the brick.png on to the BreakingOutContent project. It would be a good idea to have a class for bricks. Right click your BreakingOut project, select Add and then Class. Name this new class Brick. This is the code for the brick class.
using using using using System; System.Collections.Generic; System.Linq; System.Text;

using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace BreakingOut { class Brick { Texture2D texture; Rectangle location; Color tint; bool alive; public Rectangle Location { get { return location; } } public Brick(Texture2D texture, Rectangle location, Color tint)

{ this.texture = texture; this.location = location; this.tint = tint; this.alive = true; } public void CheckCollision(Ball ball) { } public void Draw(SpriteBatch spriteBatch) { if (alive) spriteBatch.Draw(texture, location, tint); }

} }

As you do more work with XNA you will discover what classes are in which name space. I added using statements for the general XNA framework and the XNA framework graphics classes. The classes I wanted to bring into scope are the Texture2D, SpriteBatch, Rectangle, and Color classes. The fields in this class are texture, the image for the brick, location, the location of the brick as a Rectangle, tint, the color to tint the brick, and alive, if the brick should be drawn or not. There is a property that will return the location rectangle. The constructor has three parameters. The first is the Texture2D for the texture of the brick, the Rectangle for the location, and the Color the brick will be tinted. The constructor sets the fields to the appropriate parameter and sets the alive field to true. There is a method that does nothing at the moment called CheckCollision that takes a Ball parameter. That will be used to determine if the ball hits a brick. The last method is the Draw method that takes a SpriteBatch parameter. If the alive field is true I use the Draw method of the SpriteBatch class. The next thing to do is to draw some bricks. For that you will want to add a few fields. You are going to want to know how many columns and rows of bricks you have. It would be a good idea to store the texture for the bricks. Lastly, you are going to want the bricks themselves. Add these four fields just below the screenRectangle field.
int bricksWide = 10; int bricksHigh = 5; Texture2D brickImage; Brick[,] bricks;

The first two are integer fields so that there are 10 columns of bricks and 5 rows of bricks. The next field after the integer fields is for the texture, or image, for the bricks. The last one is a two dimensional array that are the bricks. I made the brick image to be 75 pixels wide by 25 pixels high. To fit 10 columns of bricks you are going to want to change the size of the window the game plays in. XNA makes it easy to set the size of the window your game plays in. In the constructor for your game the first thing that happens is that a GraphicsDeviceManager is created. This GraphicsDeviceManager helps your game talk with the video card. The class has 2 properties PreferredBackBufferWidth and PrefferedBackBufferHeight. You can set them to be the width and height of the window your game plays in. Change the constructor for the Game1 class to the following.

public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; graphics.PreferredBackBufferWidth = 750; graphics.PreferredBackBufferHeight = 600; screenRectangle = new Rectangle( 0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight); }

What I did was set the width and height of the back buffer. I set the width to 750, because 10 times the width of a brick works out to be 750. 600 for the height of the back buffer was a bit of an arbitrary choice. I chose it as many monitors have a height of 728 pixels. 600 pixels, plus the title bar and height of the window looked good on my screen. Now would be a good time to explain some things about game programming. You will often hear the terms frame, frame rate, back buffer, and many others when people talk about games. The first term frame, what does that mean exactly? A frame of a game is where you update game objects and draw them, in simplest terms. Updating objects means moving them, checking for collisions, moving objects relative to the player's input, applying physics, and many more things. You then draw the objects in your game. The frame rate is how many frames your game processes per second. It is best to try and have your game run at a constant frame rate across all computers because computers differ in processing power. If a game runs perfect on your computer but your friend has a fast computer and you don't have your game try and run at a constant frame rate it will run much faster on your friend's computer. Similarly, if your friend's computer is slower than yours the game will run slower. How to have your game run at a constant rate across all computers is a little the scope of a beginner tutorial. What I want to mention is that XNA tries to run at a frame rate of 60 frames per second. It tries to call the Update and Draw methods 60 times per second, when possible. Part of the reason has to do with your monitor. Most monitors have a refresh rate of 60 hertz, or 60 times per second. Having your objects drawn at that rate helps reduce flicker and is smoother to the eye to view. A frame rate of about 30 frames per second is about the lowest you want to go. You will also here to term back buffer a lot in game programming circles. If you were to draw your game objects directly to the screen you will get flickering. It has to do with the drawing rate and with how the monitor updates the image. It is much better to draw your objects to an area of memory and when you've drawn all of the objects to that area of memory present that to the screen. The area of memory is called the back buffer. Presenting one image to the graphics card at a time rather than multiple images solves the problem with flickering, 99% of the time. You may still get flickering but why is beyond the scope of this tutorial. The next thing that I did was to load in the texture for the bricks. Remember that I said I would be tinting bricks. If you look at the image for the bricks on the next page it is a grey scale image. It is made up of various shades of grey. In XNA you can tint 2D objects drawn with the Draw method of the SpriteBatch class. I will explain tinting more a little later. Change the LoadContent method to the

following.
protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); Texture2D tempTexture = Content.Load<Texture2D>("paddle"); paddle = new Paddle(tempTexture, screenRectangle); tempTexture = Content.Load<Texture2D>("ball"); ball = new Ball(tempTexture, screenRectangle); brickImage = Content.Load<Texture2D>("brick"); } StartGame();

Brick Image So, we have our image. Now we need to actually create the bricks. I did that in the StartGame method. You can change the StartGame method to the following.
private void StartGame() { paddle.SetInStartPosition(); ball.SetInStartPosition(paddle.GetBounds()); bricks = new Brick[bricksWide, bricksHigh]; for (int y = 0; y < bricksHigh; y++) { Color tint = Color.White; switch (y) { case 0: tint = break; case 1: tint = break; case 2: tint = break; case 3: tint = break; case 4: tint = break; }

Color.Blue; Color.Red; Color.Green; Color.Yellow; Color.Purple;

for (int x = 0; x < bricksWide; x++) { bricks[x, y] = new Brick( brickImage, new Rectangle( x * brickImage.Width, y * brickImage.Height, brickImage.Width, brickImage.Height), tint); }

} }

So, what is going on here. The first thing to do is to create the array to hold the bricks. Back when you added the field for the bricks you were just saying, I want to use a 2D array for the bricks, you have to tell C# the size of the array you want to create. I created an array with dimensions that is bricksWide by bricksHigh. That just tells C# that is the size of the array you want to use. You now have to fill the array. When you want to loop through all of the items in an 1D array you loop from the starting item to the ending item. For a 2D array you place a loop inside of another loop, nesting loops, to loop through all items. So, the outer loop starts at 0 and loops until it is less than bricksHigh and will be used as the second dimension in the array. The inner loop starts at 0 and loops until it is less than bricksWide. You might be wondering why the loops start at 0. For that you have to go back in computer programming history. In the 1970s a programming language came out that allowed programmers to create some pretty large programs for the day called C. There are many languages today that use C-style syntax. C#, C++, and Java are three such languages. When C was introduced the inventor used 0 based arrays. The reason had to do with how memory is allocated in a computer. If he had started arrays at 1, like other languages, he was having to subtract one from the elements of the array to find their memory locations. This may seem to be a minor thing but if you're working with really large arrays and have to loop through them this subtracting of 1 will start to add up. So, inside of the outer loop I have a local variable tint that is the color to tint a brick. I set it to white initially. It is a good idea to always set variables to know values. The C# compiler will often complain that you didn't give a variable a known value though some times it won't. Giving it a value later when you are debugging you can check the value of the variable. If it is the known value and not what you were expecting then you know there is a bug somewhere in your program. There is then a switch statement based on the value of y. If it is 0 then I set tint to blue, for 1 I set it to red, for 2 I set it to green, for 3 I set it to yellow, and for 4 I set it to purple. In the inner loop is where I actually create the bricks. I assign the brick at location [x, y] to be a new brick. For the texture of the brick I use brickImage, for the location of the brick I create a new rectangle, and for the tint color I use the tint local variable. I'm going to describe how I get the rectangle for the location of the bricks. You can see that it has to do with the x and y values of the loops and the width and height of the brick image. The width and height of the rectangle are the width and height of the image. It is the X and Y values that maybe a little confusing. Think about the bricks on the screen. The first brick, in the upper left corner, will have for the X value 0. If you move one brick to the right you don't want to move just 1 pixel, you want to move 75 pixels. Moving two bricks to the right you don't want to move over 150 pixels. For the third, 225 pixels and so on. You can see that moving left to right is a function of x times the width of the bricks. Similarly, the Y value of the brick in the upper left corner is at 0. To move down one brick you want to move down 25 pixels, the height of the brick, the second down 50 pixels, the third 75 pixels, and so on. So, you see the moving top to bottom is a function of y times the height of the bricks. So, we have our bricks. Now it is time to actually draw them. You will draw them the same place that you drew the paddle and the ball, in the Draw method. Change the Draw method of your game class to the following.
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue);

spriteBatch.Begin(); paddle.Draw(spriteBatch); ball.Draw(spriteBatch); foreach (Brick brick in bricks) brick.Draw(spriteBatch); spriteBatch.End(); } base.Draw(gameTime);

There is a type of loop in C# called a foreach loop. You can use this loop to loop through collections of like objects. A 2D array is just a type of collection. In the foreach loop I loop through all of the bricks in the array. Inside the loop I call the Draw method of the brick passing in the spriteBatch to draw the bricks with. If you build and run the game now you will have the paddle, ball, and bricks all being drawn. You will see that the ball disappears when it gets to the bricks. That is because of the nature of drawing things in 2D. The order in which you draw things is important. Think of it like this. If you take a piece of paper and put it on the floor and then you put another piece of paper on top of part of it the second piece of paper hides part of the first piece. If you place another on top of them it covers, or hides, the parts of the first two, and so on. Try swapping the order in which you draw items to the following.
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); spriteBatch.Begin(); foreach (Brick brick in bricks) brick.Draw(spriteBatch); paddle.Draw(spriteBatch); ball.Draw(spriteBatch); spriteBatch.End(); } base.Draw(gameTime);

Now if you build and run your game the ball will be drawn over top of the bricks. You can see that the order in which you draw things is important. I'm going to explain about tinting things now. Colors in XNA are composed of four values. There are red, blue, green, and alpha components. For now I'm going to ignore the alpha component and focus on the red, blue, and green components. The values for these components can be represented using numbers between 0 and 255, or a byte. So, a color is made up of four bytes, including the alpha channel. As you move down from 255 to 0 you are using less of that component. To tint an object you use various levels of red, blue, and green. White is represented using full red, blue, and green. Using it to tint an object returns the original object. If you were to lower red toward 0 you would be removing red from that object. If you had 0 red and 255 green and blue the object would be tinted cyan. By tinting a grey scale image using different colors you get varying shades of that color. So, if I tint the grey scale brick using blue the grey scale brick will be made up of shades of blue.

I'm going to end this tutorial here. The plan was to get the bricks rendering. In the next part of the tutorial I plan on adding in detecting if the ball collides with a brick. I encourage you to visit the news page of my site, XNA Game Programming Adventures, for the latest news on my tutorials. Good luck in your game programming adventures! Jamie McMahon

Potrebbero piacerti anche