More servicesWindows Live
HomeHotmailSpacesOneCare
 
MSN
Sign in
 
 
Spaces home  XNA 101 .NetProfileFriendsBlogMore Tools Explore the Spaces community

Xbox Live GamerCard

BR Games
Xbox Live GamerCard
Rep:
Reputation:Reputation:Reputation:Reputation:Reputation:
Score:
1415
Zone:
Family
PGR 4Party AnimalsPGR 3Forza Motorsport 2Mass Effect

Bill Reiss

View spaceSend a message
Occupation:
Age:
Location:
Interests:
If you're interested in Microsoft XNA or just want to learn C# in a more fun way, I hope you can get something of value out of my blog XNA 101 .Net. Also check out http://www.bluerosegames.com for more XNA resources. Thanks and enjoy.
Great content and tutorials on XNA
View space
View space
(no name)
View space
blue
View space
Richard
View space
Kevin M
View space
AceTycho
View space
Dian Eduh
View space
Antina

XNA 101 .Net

Game Programming for Beginners in Microsoft's C# and XNA Game Studio.
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.1

I'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

Introduction

The 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

  • General
    • Easy To Use!
    • Support for XNA (XBOX 360 and Windows)
    • Support for Silverlight (1.1 and above)
    • Support for Managed .Net Languages In General
  • Collison
    • Concave and Convex Polygons Supported
    • Multiple Collision Geometries Per Body
    • Collision Categories For Complex Interaction Between Physics Objects
    • Collision Callback Mechanism
  • Dynamics
    • Joints
      • Revolute Joint (body to body or fixed to world)
      • Angle Joint (body to body or fixed to world)
      • Slider (Prismatic) Joint
      • Pin (Distance) Joint
    • Force Controllers
      • Linear Spring
      • Angular Spring
      • Easy To Build Custom Force Controllers (Explosions, Steering Behaviors, etc.)
  • Support and Debugging
    • Samples Framework With Samples Covering Most Major Engine Features. (XNA and Silverlight versions)
    • Debug Viewer To View All Major Physics Objects (part of samples framework)
    • User Manual (in progress)
August 26

GameFest videos now available

For 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 site

I'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 support

I 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 Online

One 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 Online

There 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 Hiatus

It'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 2007

Just 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 3D

Source 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;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace AirHockey
{
   class LedDisplay
   {
      public static Texture2D Texture;
      private int _value;
      public Vector2 Position;
      Rectangle _sourceRect;

      public int Value
      {
         set
         {
            _value = value;
            _sourceRect = new Rectangle(_value * 60, 0, 60, 90);
         }
      }

      public void Update(int value)
      {
         Value = value;
      }

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

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;
LedDisplay computerScoreDisplay;

and then we need to instantiate and initialize these objects. In the Game1.Initialize() method, add the following:

playerScoreDisplay = new LedDisplay();
playerScoreDisplay.Position = new Vector2(25, 25);
playerScoreDisplay.Value = 0;
computerScoreDisplay = new LedDisplay();
computerScoreDisplay.Position = new Vector2(715, 25);
computerScoreDisplay.Value = 0;

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();
playerScoreDisplay.Draw(spriteBatch);
computerScoreDisplay.Draw(spriteBatch);
spriteBatch.End();

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 Handling

Source 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)
{
   playerPaddle.Position = roPlayerPaddle.Position;
}
else
{
   playerPaddle.TargetPosition = playerPaddle.Position;
}
if (CollisionHandler.CheckForCollisionXZ(roPuck, roComputerPaddle) == false)
{
   computerPaddle.Position = roComputerPaddle.Position;
}
else
{
   computerPaddle.TargetPosition = computerPaddle.Position;
}

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 Paddle

Source 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;
Vector3 computerLeftPosition;
Vector3 computerRightPosition;

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);
computerRightPosition = new Vector3(hockeyTable.GoalRadius, 0, -32f);
computerHomePosition = new Vector3(0, 0, -23f);

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()
{
   Vector3 targetPosition;
   if (puck.Position.Z > -10f)
   {
      targetPosition = computerHomePosition;
   }
   else if (puck.Position.Z > -20f)
   {
      targetPosition = puck.Position;
   }
   else
   {
      if (puck.Position.X < computerLeftPosition.X)
      {
         targetPosition = computerLeftPosition;
      }
      else if (puck.Position.X > computerRightPosition.X)
      {
         targetPosition = computerRightPosition;
      }
      else
      {
         targetPosition = computerHomePosition;
      }
   }
   // make sure that the computer paddle's target position is still on the table and not jamming the puck against a wall.
   float maxX = (hockeyTable.Width / 2f) - computerPaddle.Radius - puck.Radius - 2f;
   float minX = - maxX;
   if (targetPosition.X > maxX)
   {
      targetPosition.X = maxX;
   }
   else if (targetPosition.X < minX)
   {
      targetPosition.X = minX;
   }
   computerPaddle.TargetPosition = targetPosition;
}

Notice the extra code at the end of the method. Instead of checking throughout the method to make sure that the target position is valid, we can check it once at the end and fix it appropriately.

Now to call this method. In the Game1.Update() method, after the HandleMouse(); statement, add the following:

DoComputerAI();

So now every time we enter the Update loop, the target position of the computer paddle will be recalculated. Run the program. You will probably notice a problem. It looks like the computer's AI rules are a bit too good, and you will have a hard time scoring a goal. To make it a bit easier to play, let's slow down the computer's paddle. If you look in the Paddle class, you'll see the following:

public static float MaxVelocity = 200f;

Since the MaxVelocity is static, both paddles have the same max velocity. Remove the "static" keyword from the above line. Now we will be able to set the max velocities of the paddles independently. In the Game1.Initialize() method, add the following statement:

computerPaddle.MaxVelocity = 50f;

This should give you a fighting chance. Run the program again and try your luck. One last thing...we had some test code in the Game1.HandleMouse() method to reset the puck if we right clicked the mouse. We don't need that code any more so you can remove it. Just delete the "if" statement and its accompanying conditional block.

December 14

Share what you're working on and what you'd like to hear about

I'm going out of town until Christmas, so no new entries for a while, but in the meantime, I'd like to know if there are any games you're working on. I strongly believe that the best way to learn any programming is to come up with a program you want to write and then start working through it. As you hit roadblocks, you learn new things. I haven't really done any 3D before this blog, so I have learned a lot so far and I'm looking forward to digging into shaders and effects.

Also if there are any topics that you would like me to cover, let me know and I'll try my best to work it in.

Happy Holi