![]() |
|
Spaces home XNA 101 .NetProfileFriendsBlogMore ![]() | ![]() |
Great content and tutorials on XNA
|
XNA 101 .NetGame Programming for Beginners in Microsoft's C# and XNA Game Studio.
October 09 This blog is moving... and being updated to XNA 3.0I've been asked for a while now to update these tutorials to a more recent version of XNA, and I've finally decided with the release of XNA 3.0 Beta to go ahead and do that. The new posts will be at the following address: http://www.bluerosegames.com/xna101 and the RSS feed is at: http://feeds.feedburner.com/xna101 I'll leave these posts here for reference but all of these tutorials will eventually be available at the new site. February 20 XNA Community Games on the 360, and coming to a Zune near you!Microsoft just had their GDC keynote address, and boy was it big for XNA. Microsoft has announced plans to let XNA Creators Club developers write games that any XBox Live subscriber can play, and it's community based. Games developed by the community, peer reviewed by the community, and distributed to the community. I've been dying for a viable distribution mechanism to the masses for XNA, and it looks like we are going to have one. Add to this Scott Guthrie's post yesterday about the .Net product roadmap: http://weblogs.asp.net/scottgu/archive/2008/02/19/net-3-5-client-product-roadmap.aspx where packaging of applications using the .NET framework is going to get a lot easier, and it's all good news for XNA. Add on top of that a surprise announcement of Zune game development using XNA Game Studio, and it's pretty exciting. More details here, http://creators.xna.com/whatsnew.aspx, looks like we're supposed to see a beta of a lot of this "in the spring" of 2008. October 03 Announcing the new Farseer Physics Engine for XNA Game Studio and Silverlight 1.1I've had the pleasure of working with Jeff Weber over the last couple of weeks porting the new release of his Farseer Physics Engine to Silverlight 1.1 and I'm happy to announce that Jeff released the new XNA and Silverlight versions this morning. Just to be completely clear, Jeff did most of the work on this, I was just happy to help out by doing the Silverlight port. You can get the code here: http://www.codeplex.com/FarseerPhysics/Release/ProjectReleases.aspx and you can see the Silverlight demos here: http://www.bluerosegames.com/farseersilverlightdemos IntroductionThe Farseer Physics Engine is an easy to use 2D physics engine designed for Microsoft’s XNA and Silverlight platforms. The Farseer Physics Engine focuses on simplicity, useful features, and enabling the creation of fun, dynamic games.Features
August 26 GameFest videos now availableFor those who weren't able to watch the webcasts live from the first day of GameFest, they are now available here:
There is some good information on the next release of XNA Game Studio, especially Networking support.
August 09 Relaunch of my Blue Rose Games web siteI've been working on consolidating all of my various game related materials into a single site, and I still have a bit to go, but I have updated my Blue Rose Games site at http://www.bluerosegames.com with a fresh look and more information. I'll be adding the rest of my content over the next couple of weeks. As for new XNA related information, I have a few tutorials in the works as well as a couple of other projects I can't disclose at this point, and I'll be at GameFest 2007 next week so I'm sure I'll have some information to share after that. April 10 Using Origin with SpriteBatch.Draw (Part 2)Completed source code for this tutorial: http://www.bluerosegames.com/brg/OriginTutorial2.zip In part 1 of this tutorial, I covered how to use the origin parameter of the SpriteBatch.Draw call to draw a sprite around its center and to rotate around that point. I also showed how the origin values are independent of the destination scale of the drawing. This tutorial will build upon the project from part 1. Part 2 covers another use for the origin parameter. Let's say we want to create a complex sprite, or a sprite composed of overlaying multiple textures. Ideally we would want to control the position of all of these sprites with a single position field, so that if we update that position, all drawing calls for the sprite will be updated to reflect this position. A good example of this would be if you had a warrior in a 2D RPG game, and wanted to allow the warrior to hold many different types of weapons, and you didn't want to have a separate texture of the warrior for each weapon. You could create a texture for each weapon with whitespace or padding around it so that it lines up properly with the warrior texture, but this padding takes up video memory and can make it more difficult to create the weapon textures. By giving the weapon draw call a different origin from the warrior, we can handle the offset of the overlay. In our case, I would like to overlay a shadow on the soccer ball. An overlay makes sense in this case since I don't want the shadow to rotate as the soccer ball rotates. Here is the shadow image: Save the shadow image as soccer_ball_shadow.png and add it to the project. Now in the Ball class, add the following fields: public static Texture2D ShadowTexture; public static Vector2 ShadowOrigin; These fields will store the texture and the origin for the shadow. Now in the Ball.Draw() method, add the following after the other Draw call: spriteBatch.Draw(ShadowTexture, Position, null, Color.White, 0, ShadowOrigin, Scale, SpriteEffects.None, 0); It needs to be after the other Draw call so that it is drawn on top of the other texture. So now we just need to initialize these new fields in the Game1.LoadGraphicsContent method, as follows: Ball.ShadowTexture = content.Load<Texture2D>("soccer_ball_shadow"); Ball.ShadowOrigin = Ball.Origin + new Vector2(-30, -60); Note that the shadow's origin is offset from the ball's origin by 30 pixels on the X axis and 60 pixels on the Y axis. This is because the shadow image is smaller than the ball image and needs to be shifted to the left and down to line up properly. Now if you run the program again, you should see the shadow show up on both soccer balls in the proper position. April 08 Thank you for your supportI am pleased to announce that because of my work here as well as my other work in the XNA community that I have been named a Microsoft Most Valuable Professional (MVP) for XNA. Microsoft MVPs are people who are recognized for their contributions in the community. You can read more about the MVP program here: https://mvp.support.microsoft.com/default.aspx Thank you for your continued support and I hope that this award can help me provide better support to the community going forward. April 06 Using Origin with SpriteBatch.Draw (Part 1)Source code for finished tutorial: http://www.bluerosegames.com/brg/origintutorial1.zip There are seven variations of the SpriteBatch.Draw method, and three of them accept an origin as one of the parameters. This tutorial explains some of the useful behaviors of the origin parameter and how it can help you write your game. For this tutorial, we'll create a new project called "Origin" of type "Windows Game". Now let's say we want to draw a ball on the screen. This is pretty straightforward, and is one of the first programs most people will write in XNA. Here is a simple class for drawing a ball using one of the Draw methods that accepts an origin as one of the parameters: using System; using System.Collections.Generic; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; class Ball { public Vector2 Position; public Vector2 Scale = Vector2.One; public float Rotation; public static Texture2D BallTexture; public static Vector2 Origin = Vector2.Zero; public void Draw(SpriteBatch spriteBatch) { spriteBatch.Draw(BallTexture, Position, null, Color.White, Rotation, Origin, Scale, SpriteEffects.None, 0); } } Notice that the Origin field defaults to Vector2.Zero, which will make this class, by default, behave as if an origin value wasn't specified. For the ball texture, I was playing a little bit with Microsoft's new Expression Design software which is a drawing program. I was going for a cartoony-type look for the soccer ball. Save this ball image as soccer_ball.png and add it to your project. Now to call this class from our Game class. Add the following to the Game1 declarations: SpriteBatch spriteBatch; Ball ball1 = new Ball(); In the Game1.LoadGraphicsContent method, inside the loadAllContent block, add the following: Ball.BallTexture = content.Load<Texture2D>("soccer_ball"); ball1.Position = new Vector2(150, 150); spriteBatch = new SpriteBatch(graphics.GraphicsDevice); And then in the Game1.Draw method, add this: spriteBatch.Begin(); ball1.Draw(spriteBatch); spriteBatch.End(); Run the program. It should look something like this: Now what if we want to rotate the ball so that it looks like it's spinning? Add the following line to the Update method: ball1.Rotation += (float)(gameTime.ElapsedGameTime.TotalSeconds * 3);
And run the program again. You should notice that this isn't really the effect we were looking for. The ball is rotating around its top left corner intead of around its center. In order to make the ball rotate around its center, we can set its origin to half of its width and half of its height. In the Game1.LoadGraphicsContent method, add the following: Ball.Origin = new Vector2(Ball.BallTexture.Width / 2, Ball.BallTexture.Height / 2); Now run the program again. The ball should now rotate about its center. The ball is also being drawn higher and more to the left of the screen, since by setting the origin, the position of the ball also takes the origin into account, so the position of the origin is at the position specified in the draw call. OK so now for a second ball that's scaled by a power of 2. Declare a ball2 in the Game1 class as follows: Ball ball2 = new Ball(); In the Game1.LoadGraphicsContent method add the following: ball2.Position = new Vector2(500, 300); ball2.Scale = new Vector2(2, 2); And in the Game1.Update method add this: ball2.Rotation += (float)(gameTime.ElapsedGameTime.TotalSeconds * 4);
And finally in the Game1.Draw method, add the draw call for the second ball. ball2.Draw(spriteBatch); Run the program again, is this surprising? We didn't scale the origin but the ball still spins around its center. This is because the origin parameter is specified in source pixels. This is a bit confusing at first, but is nice because no matter how we scale the destination, the origin will stay in the same relative position. This is very useful if we're scaling our sprites based on game surface resolution to fill the screen. March 09 Moving Forward
I've been having a problem with how I have been doing this blog so far, because I kind of painted myself into a corner by adding on to the current project with each lesson. So when I haven't been ready to add a new feature to the current game, I had to wait to post a new entry. This resulted in a long delay between blog entries. So going forward, this is how I'm going to try to manage the blog. If I have one-off topics to post about, like the enhancement to the Font Sample, I'll post them without a lesson number. When I post the final few tutorials in the Air Hockey sequence, I'll number them so that it is clear as to the order of those tutorials. Thank you for your continued support, and I hope that these changes will allow me to post about unrelated topics while preparing the final lessons in the Air Hockey game. Improving the Font Sample on Creators Club OnlineOne of the new samples on the XNA Creators Club Online (http://creators.xna.com) is a sample for rendering text in your game. I had created something very similar to this myself, as have a few others, since it's a pretty common need. The Font Sample allows you to create a bitmapped font or import a font from the fonts on your computer and then use it to draw text. This functionality is going to be in the April XNA update, but for now it's a good alternative. There was one thing, however, that was missing from the Font Sample that I had in my text rendering code that I thought would be useful, and that is a way to rotate the text. If you add the following method to the Font class in the Font sample, you will then be able to pass a rotation value (in radians) into the Draw method. public void Draw(string text, Vector2 position, float scale, float rotation, Color color, SpriteBatch spriteBatch){ Vector2 origin = Vector2.Zero; Vector2 curOrigin = Vector2.Zero; foreach (char ch in text) { // Look up what font glyph corresponds to this Unicode character. int index; if (!characterMap.TryGetValue(ch, out index)) index = glyphData.Count - 1; // Look up what part of the texture represents this character. Rectangle glyph = glyphData[index]; Rectangle cropping = croppingData[index]; curOrigin.X = origin.X - cropping.X; curOrigin.Y = origin.Y - cropping.Y; // Draw the character. spriteBatch.Draw(textureValue, position, glyph, color, rotation, curOrigin, scale, SpriteEffects.None, 0); // Move the origin further to the left, because the origin is relative to the current letter. origin.X -= cropping.Width; } }
If you wanted to rotate around a different point, you could create a new method which would take the origin as one of the parameters and use that instead of Vector2.Zero as your starting origin. Here is a screen shot of a modified FontSample program with some text rotated: XNA Creators Club OnlineThere is a new web site from the XNA team called XNA Creators Club Online. The url is http://creators.xna.com and it includes code samples, forums, links to resources, a new Starter Kit, and more.
Go check it out! January 27 Sorry for the HiatusIt's been a while since I've posted here and I just wanted to let everyone know that I haven't abandoned this blog.
Real life (work) has gotten in the way a bit (wish I could do this full time but something has to pay the bills) and I've been spending all of my free time creating an XNA project called XNAStage. You can find the XNAStage project at http://www.codeplex.com/xnastage and it basically came from the need to do splash intro screens and dialog boxes in an easier way.
Basically what XNAStage does is let you give a start state and an end state and a start time and an end time for a sprite and XNAStage will fill in the gaps. This is typically called "tweening" in Flash or other similar technologies. For example, you can give a starting position and an ending position for a sprite and it will move smoothly between the positions and stop once it gets there. The same can be done with color tinting and fading, and pretty much anything else you can think of a tween for. You can get some samples of XNAStage here: http://www.bluerosegames.com/brg/XnaStageSamples.zip
The next lesson will incorporate the XNAStage library into the air hockey game and we will use it for a game over screen. January 09 South Florida .NET Code Camp 2007Just a quick note letting everyone know that I'll be doing a session at the South Florida .NET Code Camp on February 10, 2007 in Miramar, Florida. If you've never been to a Code Camp, it's an unusual event in that local speakers (especially those with little or no speaking experience) are given first choice, followed by regional and big name speakers, and finally the regular Microsoft speakers. I'll post up my presentation materials and samples afterward. For more information and registration, go to http://codecamp07.fladotnet.com/codecamp.aspx, hope to see you there! January 06 Lesson 26: Mixing 2D and 3DSource for this lesson: http://www.bluerosegames.com/brg/XnaLesson26.zip When developing a 3D game, there will be occasions where some visuals you want to display are easier done with 2D graphics. It may be a scoreboard, a heads-up-display, a health meter, or crosshairs for a gun, or something totally different. In XNA, 2D and 3D graphics can be displayed simultaneously as long as certain care is taken. For the air hockey game, let's use 2D sprites to display the score. I have generated this image which is meant to look like numbers on an LED display:
The image has a trasparent background. Save the image to your computer as lednumbers.png and add it to your project. Notice that all of the numbers are in the same image. This technique is one of the easiest and best ways to implement sprite animation. Each part of the image could just as easily be different positions of a running plumber in a red hat or the frames of an explosion. So just like in our 2D examples previously, we need a SpriteBatch object. In the Game1 Field declarations add the following: SpriteBatch spriteBatch; and then in the Game1.Initialize() method, we can instantiate the SpriteBatch object as follows: spriteBatch = new SpriteBatch(graphics.GraphicsDevice); Now we need the logic to draw the numbers. As we did with the BouncingBall class, let's create a class to hold the logic related to drawing the numbers. Create a new class called LedDisplay. The code for the LedDisplay class should look as follows: using System; namespace AirHockey public int Value public void Update(int value) public void Draw(SpriteBatch spriteBatch) Some of this code should look familiar. There are a couple of differences, we are using a different variation of the SpriteBatch.Draw() method which accepts a source rectangle which defines what portion of the source texture we want to draw. The source rectangle is determined by the value of Value, which will control which number is displayed. Since each "frame" in our animation is 60 pixels wide and 90 pixels high, the X value of the top left corner of the source rectangle is equal to the width of a frame times the value to display. When Value is 0, X is 0. When Value is 1, X is 60, and so on. So from the Game1 class, by simply setting the Value property, the source rect parameters will be automatically set to the correct values. Now in the Game1 class, we need two LedDisplay objects, one for each score. In the Game1 Field declarations add the following: LedDisplay playerScoreDisplay; and then we need to instantiate and initialize these objects. In the Game1.Initialize() method, add the following: playerScoreDisplay = new LedDisplay(); And in the Game1.LoadGraphicsContent() method, we need to load the Texture that contains the number and associate it with the LedDisplay class. Since it's a static Field in the class, we only have to associate it with the class and not a specific instance of the class. So in the Game1.LoadGraphicsContent() method, inside the conditional block, add the following statement: LedDisplay.Texture = content.Load<Texture2D>("lednumbers"); Now to draw the scores. In the Game1.Draw() method, just before the base.Draw(gameTime); statement, add this: spriteBatch.Begin(); We haven't yet added the logic to tie the actual scores to these objects, but we can at least see how it looks dsiplaying zeroes. Run the program and see how it looks. Well the numbers look good, but what happened to our paddles and table? The answer is that the SpriteBatch uses render state settings that are very efficient for drawing 2D sprites but are not capable of correctly rendering 3D models. When spriteBatch.Begin() is called, the spriteBatch object set the render state settings to what it likes best, and then leaves them that way. Fortunately we can tell the SpriteBatch to set things back the way they were after it's done (when SpriteBatch.End() is called). To do this, change the SpriteBatch.Begin(); statement in the Game1.Draw() method to look like this: spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState); The first two arguments are just set to the defaults, the important one is the third. It tells the SpriteBatch to save off the current render settings and then restore them when done. Now run it again and it should look better. So now to update the scores when someone scores. In the Game1.CheckForScore() method, just after the computerScore++; statement add the following: computerScoreDisplay.Value = computerScore % 10; The % sign is a "mod" operator where the value is divided by the number specified and the result is set to the remainder of the division. Since we only have a single digit display, we can only display 0 to 9. By doing this mod operator, the display will start over at 0 when it gets greater than 9. This won't matter later since the first player to 7 will win, but for now this will keep the display from displaying invalid results. Now do the same for the player's score by adding the following line after the playerScore++; statement: playerScoreDisplay.Value = playerScore % 10; Now when you score a goal (or the computer scores) you should see the score get updated. The final result should look something like this: January 04 Lesson 25: Improving the Collision HandlingSource code for this lesson: http://www.bluerosegames.com/brg/XnaLesson25.zip Sometimes (actually pretty regularly) you get to a point in a program where you're just not happy with how something works. In my effort to keep the collision handling simple, I had the unfortunate side effect of the puck getting jammed frequently against the wall by the paddle. The code in this lesson will attempt to alleviate some of this. The changes are just a few lines of code, but sometimes a small change can make a big difference. In the Game1.Update() method, add the lines in red: if (CollisionHandler.CheckForCollisionXZ(roPuck, roPlayerPaddle) == false) These new lines make it so that if there will be an overlap by repositioning the paddle, we will instead set the paddle's TargetPosition to the current position. By doing this, we give the puck the rest of the Update cycle to get out of the way. If we're near the end of the Update cycle, we may need the next cycle as well to clear the puck, but it's quick enough that the player won't notice. January 02 Lesson 24: Controlling the Computer PaddleSource code for lesson 24: http://www.bluerosegames.com/brg/XnaLesson24.zip In Lesson 21, we added code to control the player's paddle based on mouse movements. In this lesson, we will add some simple logic to control the computer's paddle based on the puck's position. Artificial Intelligence (AI) is a general term which is used to describe the logic put in place to make the computer exhibit behaviors that mimic the behaviors of living creatures. In our case, we need to mimic the behavior of an air hockey player. First, we need to come up with a set of rules that we can turn into code. These rules can be very complex based on how intricate the behvior that we are trying to mimic is. If we wanted to mimic a professional air hockey player, we would have to research all of the advanced techniques involved in air hockey and write code to perform these techniques. For this beginner game, we'll keep the rules much simpler. These are the rules we will use: 1. If the center of the puck is anywhere on the player's side of the table or in the first 10 inches of the computer's side, set computer paddle's target position to its "home defensive position" at (0, 0, -23). 2. Otherwise if the puck is in the area 10 inches away from center ice to 20 inches away from center ice, set the computer paddle's target position to the puck's center position. 3. Otherwise do the following: a. If the puck is to the left (from the player's perspective) of the "left defensive position" at (-hockeyTable.GoalRadius, 0, -32f) set the computer paddle's target position to its "left defensive position". b. Otherwise if the puck is to the right (again from the player's perspective) of the "right defensive position" at (hockeyTable.GoalRadius, 0, -32f) set the computer paddle's target position to its "right defensive position". c. Otherwise set the puck's target position to the "home defensive position". Now to implement these rules. First let's define 3 new Vector3 Fields in the Game1 class to hold the home, left, and right defensive positions. In the Game1 declarations, add the following: Vector3 computerHomePosition; and in the Game1.Initialize() method add the following to initialize the values of the computer defensive positions: computerLeftPosition = new Vector3(-hockeyTable.GoalRadius, 0, -32f); Now that we have these positions defined and initialized, we can add a method to the Game1 class which will execute the rules we defined above. Let's call the method DoComputerAI() and it will look like this: void DoComputerAI() | ||||||||||||||||||||||||||||||||