2D Pong using the Microsoft XNA Framework IntroductionIn this tutorial, I build a simple Pong game using the new XNA Framework Beta 1. I'm assuming that whoever is following this tutorial has a basic understanding of C# and OOP, and knows how to use the Visual C# Express IDE. Pong is a very simple game; the player controls a paddle on one side of the screen and plays against another paddle controlled by the computer. A ball is hit back and forth between these 2 paddles; a player gains a point if the other player does not manage to hit the ball. Creating your ProjectOpen XNA Game Studio Express and start a new Game by going to File > New Project.. and select Windows Game (XNA). Give the project a title such as Pong and click OK:
Go to File > Save All and save the whole project:
Setting up the GameXNA Game Studio Express creates a project for us with all the required classes and methods that initiate a DirectX enabled window. The Game1.cs file creates a "game" by inheriting from the Microsoft.Xna.Framework.Game class, andcontains the Update() and Draw() methods in which we will code the game's logic and graphics. More on this later. Let's now set up some basic things such as the window size of the game and any graphics we will be using. Click on the graphics component in the Game1.cs Designer window and modify the BackBufferWidth and BackBufferHeight to 800 and 600 repsectively. The game will be windowed, no full screen for now: We now need to add any graphics that we will be using in the game to the project. I created a simple paddle and a simple ball graphic in Photoshop CS2 and saved them as .png files. The files are included with this tutorial in a folder called "textures" and you can load them into the project by right clicking on the Project name in the Solution Explorer window and going to Add > Existing Items. Browse to the folder containing the PNGs and add them to the project. Adding the images to the project is just the first step - now we need to load them as textures so that they can be displayed on screen. Go to the Code view of the Game1.cs file and add the following declarations just before the constructor of the class:
Here we have declared three Texture2D objects, two for the paddles and one for the ball. We also need a SpriteBatch object which will be used to display my sprites (2D textures) on the screen. Now we need to create the Texture2D objects from the PNG images in our project. We will create a seperate method for this, called LoadResources(), so add the following code after the constructor of the class:
The Texture2D.FromFile method takes 2 parameters in this case: the device on which the textures will be created, and the source of the PNG files, which are located inside our project. "../../" indicates two folders up from the current folder, which is the "Debug" directory of our solution. We also create the SpriteBatch object. Of course, coding the method and never calling it won't do much for us. Resources have to be loaded when the game starts, as well as when the graphics device is reset. Add the following code just before the LoadResources() method:
In this code we override the Game.OnStarting() method and create an event handler for when the device resets. Inside each method we call the LoadResources() method so that the textures are created when the game starts and when the device resets. Paddle and Ball ClassesIn order to better manage the components inside our game, two classes will be created which will represent the paddles and the ball in the game. First we will create the Paddle class, so add a new class to the project (right click on Project name > Add > Class) and add a new class Paddle.cs. Delete all the code inside the Paddle.cs file and replace with the following code:
This class is very simple to understand: a paddle will be identified by its position (Point pos) and the speed and direction it is travelling in at the moment (int speed). The constructor of the class takes an X,Y co-ordinate which determines the initial location of the paddle. Also the default speed of the paddle is 3, which means it will move 3 pixels each time. The Ball class is very similar, so just create a new class (Ball.cs) and paste the following code inside it:
The ball is also defined by its position, but it has two speeds and direction this time, the vertical and horizontal speeds. The speed for these positions is between 3 and 6, and the Random class is used to generate a random speed and direction when the ball is created. These speeds will be hardcoded in this game to keep it simple; it would be a good idea to put these values inside an XML file and make the game customizable at run-time.
Finally, we just declare two paddles and a ball in the Game1.cs file, just before the constructor.
In order to create these objects, we will use a method which simply instatiates the objects and passes some default starting values to them. Create a method ResetGame() just after the constructor:
Also we need to call this method as soon as the game starts, so add a call to it inside the constructor:
Great! Our game is setup and all we have left is to draw the graphics on the screen and move them about. Drawing Textures on ScreenNow we have to draw the textures on the screen, and the best place to do this is inside the Draw() method of the Game1.cs class. Some code already exists inside the method (created by the IDE), which clears the graphics device using a specific colour and prepares the device for output. You can read all about this in the documentation that comes with XNA (Application Model overview in Help > Contents > XNA > Programming Guide). In order to draw our graphics, we need to use the SpriteBatch class. Place the following code inside the Draw() method of Game1.cs, just after the "//TODO: Add your drawing code here" line:
The SpriteBatch.Begin method indicates that we want to start drawing textures (sprites) to the device, and the SpriteBlendMode.AlphaBlend property enables transparency support for the textures. We need this because the PNG graphics have transparency in them. The SpriteBatch.Draw method adds a sprite to the list of sprites to be drawn, and takes as parameters the Texture2D object representing the sprite, the location and size of the sprite (as a Rectangle) taken from the Paddle or Ball objects, and a colour to use for channel modulation. Use Color.White to draw the sprites in their original form. SpriteBatch.End ends the drawing and restores the device. If you compile and run your program (F5), you should get a Window with 2 paddles on each side and the ball in the middle. Of course nothing is moving yet, so we'll get to that right away. Keyboard Input and MovementThe paddle on the left will be the player's paddle, which he will control using the arrow keys on the keyboard. Obtaining user input from the keyboard is simple in XNA. The best place to put code that performs all your game logic is the Update() method of the Game1.cs class. We will create a method UpdatePaddles() which will be called from the Update() method. The first thing we do in the UpdatePaddles() method is to check for an user input from the keyboard. Add the following code after the Update() method:
We still haven't declared a KeyboardState object in the class, so we will do that now. We also need a Random object for later on, so declare these two just before the constructor of the Game1.cs file:
Also make sure you add a call to the UpdatePaddles() method inside the Update() method, just after the "// TODO: Add your game logic here" line:
The Keyboard.GetState method returns the current state of the keyboard. Then we use the KeyboardState.GetPressedKeys method to obtain a list of keys that are being pressed at the moment. Finally we loop through all the keys that are pressed and perform anything we need according to which key is pressed. In the case that the Up key is pressed, then we move the paddle upwards according to its speed. If the Down key is pressed, then we move the paddle downwards. Also we check if the Escape key is pressed and we Exit the game if so. If you run the program, you should be able to move the paddle up and down using the Up and Down arrow keys.
Now all we need to add to the UpdatePaddles() method is some code that moves the other paddle according to the position of the ball, and also perform some bounds checking to make sure the paddles do not move off-screen. Add the following code to the UpdatePaddles() method:
The code simply checks the Y values of the paddles in relation to the screen and the ball to make sure that the paddles never move off the screen and the computer's paddle follows the ball.. Read through the code, it is not difficult to understand. There are some hard-coded constants involved, such as a border of 10 pixels so that the paddles never touch the sides of the Window.
We also need to move the ball around, and we will do this inside a new method, UpdateBall(). Add the following code to the Game1.cs file, just after the UpdatePaddles() method:
In this code we simply update the positions of the ball according to the speeds inside the Ball object. We also check whether the ball has hit the top and bottom borders and change its direction if so by negating the vertical speed. Make sure you add a call to the UpdateBall() method in the Update() method, just after the call to UpdatePaddles():
If you run the program now, everything should be moving, but you'll see some weird behaviour. All we have left is to implement collision checking between the ball and the paddles. Collision DetectionAll we have left to do is to detect when the ball collides with one of the paddles, and bounce it off. This code will be placed inside a method CheckCollisions(), which you can insert into the Game1.cs file just after the UpdateBall() method:
Make sure you add a call to the CheckCollisions() method in the Update() method, just after the call to UpdateBall():
The CheckCollisions() method works like this: First we check whether the ball is moving left or right, so that we only check if the ball is going to collide with one paddle or the other. Then we check if the ball's X position has surpassed the paddle's X position, and if so, we check if the ball was in the range covered by the paddle, using the Y positions. Finally if the ball has hit the paddle, we calculcate random values for the speed of the ball and change its direction (so no fancy collision physics here). If not, then we call the ResetGame() method and the game resets. In a normal game you would increase scores, check for any power ups etc. but we're keeping it simple here. ConclusionWell, that's it folks. The new XNA framework really makes it easier to start programming in DirectX, and I hope the XNA team keep doing a good job.We only saw a small part of it, and of course the game we made is simple and can be much elaborated upon. This was also my first ever tutorial, so i hope you liked it and found it useful. Email me here with any comments or suggestions you might have. |