Wednesday 22 October 2008

J2ME Pong Game - Walkthrough & Tutorial

Ok, first things first, a brief explanation of what we are going to be doing, but before I do let me say this is the first tutorial I have ever written, I usually find good ones and re-write them! I make no apologies for my style of writing or the flow of this tutorial.
Ok, so we are going to create a pong clone that will run on the Netbeans emulator. The tutorial will cover some J2ME game programming basics such as the Game Canvas and some of its classes, the use of images and sprites, sprite frame sequences in order to animate the sprites, multi-threading and collision detection. Now that might seem a lot but I can promise you it takes no more than two classes and less than 400 lines of code (which I think is pretty damn good to be honest).
The way I have written the game is to encourage flexibility and good programming practice, I will go into detail about plenty of things and I strongly recommend you read the whole tutorial line by line. When appropriate, I will provide screenshots and the source code will be given bit by bit. It should take you no more than an hour to complete. So, without further delay, let me present MobilePong, your next (and possibly first) J2ME game.
Okay, lets start Netbeans and create a new Mobile Application. The usual rules apply, create a completely empty project, call it MobilePong, do not create a visual MIDlet and make sure you select MIDP 2.0 as your target rather than 2.1.
Now, you NEED to download this zip file. The zip file contains three images in PNG format. One is the pong table that looks more like an air hockey table, one image contains the ball that will be used and one is an image of 8 different coloured pong paddles.
Now you need to navigate to your MobilePong project folder, this is usually “My Documents\NetbeansProjects\MobilePong. Now, when your there, create a new folder called “res” and put the three image files into the root of the newly created directory. The “res” stands for resources and this is where you should get into the habit of putting any external images and sounds you may use in your J2ME game.
Okay, back to Netbeans. Now we want to tell the project that we want to use the res folder for some of our resources. Click on the + symbol next to your project if it isn’t already expanded and right click on the Resources tag and select “Add Folder”.

You will be prompted to add the folder, just navigate to the res folder you just created with the three images in it.
Notice that we haven’t even created a class or MIDlet yet so perhaps we should do that now! Right click on the source packages and select New –> Midlet. Name the MIDlet “MobilePong” and if you understand how packages work you can create a package while you are at it but it will not affect the tutorial. I will assume you DO NOT but if you did, remember that you must make that your first declaration in every class including the MIDlet.

1: package packagename;

Okay, so we have create the MIDlet, but there’s not going to be too much going on there as we will have one main class that runs the game. So, lets create a new Java Class. Right click the source packages like you did for creating the MIDlet but this time it’s New –> Java Class. We will call this class “MainGame”.
Okay, if you made it this far, well done, I can promise you that everything from here on is is coding and no more setting up and messing about. The MainGame class is going to be responsible for displaying the graphics for our game so we need to import some libraries and extends the game canvas.
Right before the class declaration, import the required libraries and extends the class to use the GameCanvas like this

1: import java.io.*;
2: import java.util.*;
3: import javax.microedition.lcdui.*;
4: import javax.microedition.lcdui.game.*;
5:
6: public class MainGame extends GameCanvas
7: {
8:
9: }

Now you should notice a red warning line under the class declaration on line 6 saying that GameCanvas(boolean) cannot be declared as (). What this means is that the GameCanvas constructor (which you do not need to worry about) take a boolean parameter that tells it whether or not to suppress regular Canvas key presses. Don’t worry about this too much but basically, we need to create a constructor for MainGame and tell it that we want to suppress key events.
To do this we put the following code after the class declaration (line 8).

public MainGame()
{
super(true);
}

Okay, that got rid of the error so lets start thinking about what we use a constructor for. A constructor (like above) uses exactly the same name as the class name, is public 99.9% of the time and never has a return type, that’s how the compiler determines it is the constructor. Imagine the constructor being the place that all your initial setup code will go since it is only called whenever a new instance of the class is called (which in this example will be only once, we can only play one game at a time after all)!
This makes the constructor the best place to load in our images and change them into Sprites! So, just under the super(true) create a call to a method called loadImages() and create a method called loadImages after the constructor that is public and has a void return type.
Your code should now look like this

1: public class MainGame extends GameCanvas
2: {
3: public MainGame()
4: {
5: super(true);
6:
7: loadImages();
8: }
9:
10: public void loadImages()
11: {
12:
13: }
14: }

So, everytime we start a game, loadImages will be called and in this method we can put all our code to load as many images as we want! Of course, the images and the sprites will be accessed from outside the loadImages() method so we need to create a link for all the images we are going to be loading in the variable declaration part of the class which is before the constructor but after the class name. We have three images to load, the table, the ball and the paddle so lets do that now

// Links to the Images we load
private Image tableImage;
private Image paddleImage;
private Image ballImage;

public MainGame()
{
........
}
......
}

The dots represent code that is already written and the constructor is only in there to let you know where to put the code so it’s just the three Image variables you want to put in your code!
This DOES NOT create the image, it simply tells the compiler you have three Images that you are calling “pongTable”, “paddleImage” and “tableImage”. We will create them in the loadImages() method.

public void loadImages()
{
try
{
//Remember to load the res folder into netbeans
tableImage = Image.createImage("/PongTable.png");
paddleImage = Image.createImage("/Paddle.png");
ballImage = Image.createImage("/PongBall.png");
System.out.println("Loaded Images!");
}
catch(IOException ioex)
{
System.err.println(ioex+" Error loading images");
}
}

Everything in this method is surrounded with a try catch because it has to make sure the files can be found. If you haven’t added the res folder to the project, you would get an error when trying to run this. Creating an image is simple, you make the image variable you already declared equal Image.createImage(), real simple! I have put a print statement so that when we run it we can see that the images were loaded fine.
If you try and run this now IT WILL NOT WORK. That’s because we haven’t told the MIDlet to load the MainGame class so we’ll do that now. In your MIDlet code, declare a variable called myDisplay of type Display and a variable called theGame of type MainGame, just like this

public class MobilePong extends MIDlet
{
private Display myDisplay;
private MainGame theGame;

.....
}

Again, these do nothing on their own so we need to get the display from the phone, create a new instance of the MainGame class (which will load all the images now) and then set the display to the MainGame instance called theGame

public void startApp()
{
myDisplay = Display.getDisplay(this);
theGame = new MainGame();
myDisplay.setCurrent(theGame);
}

If you run this code you should see the emulator pop up and once you start the MIDlet the screen should appear white but Netbeans should write a statement saying the images were loaded. Congratulations, you have just imported your images to the game.
I know this is rather tedious but we’re getting to the good bits and if you can stop yourself from trying to run the code, the next time we run it we will have something to show on screen!
Before we move on, I said I was going to explain sprites (which I will) but one of the advantages of using the GameCanvas is that you can use something called a LayerManager where you can store all your sprites and, rather than drawing every single sprite you have in your game (which would get annoying with even a slightly larger game than this) we can draw the LayerManager to the screen and the phone will take care of the rest. So, you may have guessed, we’re going to create a LayerManager which takes only two lines of code.
We need to declare it just below where we declared our images and we need to instantiate it just after we load the images (in our constructor remember?).


1: private Image ballImage;
2: private LayerManager manager;
3:
4: public MainGame()
5: {
6: super(true);
7: loadImages();
8: manager = new LayerManager();
9: }

Line 2 is where we say we are using a LayerManager called “manager” and line 8 is where we create it. Now all we have to do is add our sprites to it. Well, I guess we should make some sprites then. Just under line 8 add a new method called loadSprites() and create the method for it, making it public with a void return type, just like so . . .

public void loadSprites()
{

}

Don’t forget to put the call just under the “manager = new LayerManager()” in the constructor.
Ok, a little on sprites. A sprite is just taking the image we previously created and making it a Sprite. There are some differences we will go into with the paddle because we are going to animate it but for now, just follow along. Now we need to declare them like we did with the images. So just under the images and LayerManager declaration we need to put

.....
private Sprite tableSprite;
private Sprite paddleSprite;
private Sprite ballSprite;

public MainGame()
{
.....
}
......
}

and in the loadSprites() method we just created, put the following code

tableSprite = new Sprite(pongTable);
paddleSprite = new Sprite(paddleImage, 10, 50);
ballSprite = new Sprite(ballImage);

Now, what we are going to do is add the newly created sprites to the our layer manager. Add the following under that code but still in the loadSprites() method

//Remember the z-order
manager.append(ballSprite);
manager.append(paddleSprite);
manager.append(tableSprite);

If you’re wondering why the paddleSprite looks slightly different, I’ll explain that really soon, just go with it just now.
Notice the comment? “What is Z-Order” I hear you scream! Well, the Z-Order is the order in which things appear on top of each other. We don’t want the table to cover the pong ball or the paddle, we want the ball and paddle to sit on top of the table so we have to make sure we add them to the manager in order from closest to furthest away. Since we will be performing collision detection on the paddle and the ball, they could be swapped around but the table needs to be added last.
Now, we can almost get something running on the screen. First, we’re going to need to go over Threads and how they work. In J2ME, when you want a class to be executed in a separate thread you need to implement an Interface called runnable.
At your class declaration add the words “implements Runnable” like this

public class MainGame extends GameCanvas implements Runnable
{
// Links to the Images we load
private Image tableImage;
private Image paddleImage;
.....
}

Notice the new “implements” statement at the end of the class? You should have a red line that, when you hover over it, tells you that you do not override the abstract method run(). This means that whenever you use this implementation, you MUST write your own run() method and create a variable link to the Thread just like you do with the Images and the Sprites so lets do that now !
Underneath the constructor we are going to create two new methods called “start” and “run” both are public and both have a void return type. The start method is just a convenience method that we can call from the MIDlet so don’t worry about that too much just now.

public MainGame()
{
super(true);
loadImages();
manager = new LayerManager();
loadSprites();
isGameRunning = true;
}

public void start()
{

}
public void run()
{

}

Don’t forget the link to the Thread at variable declaration

......
private Thread gameThread;

public MainGame()
{
.....
}

Now, what happens is that all the code is executed in a separate thread from the rest of the code. In practice, the way we are using it is a little bit of overkill but it gives you a good idea of how to use Threads. Imagine you had a large and complex amount of network data to send from one computer to another but you also have a large amount of processing of raw data as well. Do you really want to stop processing the raw data in order to send and receive a message from a server? No, you create a separate thread, do the time consuming networking in that and continue with the processing of raw data as well.
We want the start method to start the thread and run it so if we look back to the MIDlet again, we want to add this code just under the display code that we wrote a while ago we want to call the start method.

1: public void startApp()
2: {
3: myDisplay = Display.getDisplay(this);
4: theGame = new MainGame();
5: myDisplay.setCurrent(theGame);
6: theGame.start();
7: }

The only addition here is line 6 where we call the start method. At this moment, the start method is empty so lets code it! Back to the MainGame class.

public void start()
{
gameThread = new Thread(this);
gameThread.run();
}

I’ll talk these two lines through. The first instantiates the Thread object. The Thread constructor takes one parameter called “this”. That is just a fancy way to say “I want this class to be the class that is run in a thread”.
The second line calls the run method which we have created but not coded yet so we will do that now.
So, in this run method is going to be your game loop. The game loop will do what you would expect a game loop to do.
1) Check to see if there is any user input on the phone’s keypad.
2) Move any objects that need to be moved.
3) Check if there is any collisions between objects that you would want to act on (especially since the step before we have just moved things).
4) Update the mobile display.
There is an extremely easy control function that can accomplish all that and most of you will have encountered it…..it’s called the mighty while loop!
Let’s put a boolean value in our variable declarations (where we declare our images and sprites) and let’s call it “isGameRunning” and set it to the value true in the constructor, because, if we are creating an instance of MainGame then we can assume that we want the game to be running!

1: private Sprite ballSprite;
2: private boolean isGameRunning;
3:
4: public MainGame()
5: {
6: super(true);
7: loadImages();
8: manager = new LayerManager();
9: loadSprites();
10: isGameRunning = true;
11: }

Notice the additions in line 2 and line 10. We add the boolean variable called “isGameRunning” and set it to a value of TRUE in the constructor.
Now, back to the run method. There’s gonna be a few lines of code so struggle through and read the explanation afterwards to get a better understanding of it.

1: public void run()
2: {
3: Graphics g = getGraphics();
4: while(isGameRunning)
5: {
6: checkUserInput();
7: moveObjects();
8: collisionDetection();
9: updateGameScreen(g);
10: flushGraphics();
11: try
12: {
13: gameThread.sleep(60);
14: }
15: catch(InterruptedException ie)
16: {
17: System.err.println(ie);
18: }
19: }
20: }

Let’s go through this line by line and make sure you understand this before moving on.
Line 3 is where we get the graphics adapter for your phone. This isn’t as complicated as it sounds, it just gives you a way to draw Images, Sprites and basic shapes if you wanted. The only thing you need this for is to pass it as a parameter to another method that we haven’t coded yet.
In line 4 we start the game loop. Remember, in the constructor we set this to true so it will run indefinitely until we tell it otherwise and set it to false.
Lines 6, 7, 8 and 9 are calls to method that we are going to create in just a minute so don’t worry about them just now but the names should suggest what they will do.
Line 10 is a method that is used because we extend the GameCanvas right at the start. Flushing the graphics is common in game programming because we are using double buffering. Double buffering means we are drawing every frame to a screen that isn’t actually shown on the phone. Once we have drawn everything onto the screen, then we show it in one go. If we didn’t the screen would tear as we updated because not everything is updated at the same time and this is why you get “tearing” in games. So flushGraphics() basically takes the frame and sticks it onto the phone’s screen. It is imperative to put this just after updating your game screen.
Lines 11 to 18 is a try catch that makes the thread take the human equivalent of a breather. This game isn’t demanding at all so we don’t want to run this process as fast as we can. We tell the thread to sleep for 60 milliseconds by passing the number 60 as a parameter to gameThread.sleep(60);. You can of course change this and you would see your game speed up or slow down depending on the number you set.
Now I know I promised you that things would be shown on screen so I will keep to my promise. All you need to do is create three empty methods and one last method that draws all your hard work.
If you look at the code you have just written we need to create a method for:
1) checkUserInput()
2) moveObjects();
3) collisionDetection()
4) updateGameScreen(g)
All these methods are public and return void but let’s do the first three first then we’ll talk about the fourth one and get something on screen.
Underneath the last method we wrote in the MainGame class (which should have been loadSprites) we will put the rest of these methods

public void loadSprites()
{
tableSprite = new Sprite(tableImage);
paddleSprite = new Sprite(paddleImage, 10, 50);
ballSprite = new Sprite(ballImage);
}

public void checkUserInput()
{

}

public void moveObjects()
{

}

public void collisionDetection()
{

}

This just stops errors from appearing when we run the code (which will be very soon, I promise).
Last method to implement is the updateGameScreen method which we will put just under the collisionDetection(). If you look at the call we make to it in the run method, we pass it “g” which is the Graphics Adapter so it’s very similar to the other but with one parameter. Also, in the method, since it is just the sprites we want to draw, and remember, all the sprites are loaded into our manager, we want to draw the manager and let the phone do the hard work

public void updateGameScreen(Graphics g)
{
manager.paint(g, 0, 0);
}

Ok, I know you don’t really want me to explain anything just now so run the code!
You should see something similar to this

Notice that with one call of the manager’s paint method, everything was drawn to screen. We pass the (Graphics g) parameter so it has a link to the graphics adapter which is, in turn, passed to the managers paint method with “manager.paint(g, 0, 0);” Now the “g” part is easy enough to understand. The “0, 0” part is what part of the screen it should update. We want it to update the whole screen so we set it to 0, 0 which is top left on the mobile phone. If we had a score counter at the very top we might want to set it to 0, 20 and update the score a different way but that’s outside the scope of this particular tutorial.
Before I move on, I’m going to post the whole code up to this point in case anyone is having trouble or daft errors (I mean daft as in spelling mistakes and forgetting to initialise variables because we all do it!).
So, first, the MIDlet.

1: import javax.microedition.midlet.*;
2: import javax.microedition.lcdui.*;
3:
4: public class MobilePong extends MIDlet
5: {
6: private Display myDisplay;
7: private MainGame theGame;
8:
9: public void startApp()
10: {
11: myDisplay = Display.getDisplay(this);
12: theGame = new MainGame();
13: myDisplay.setCurrent(theGame);
14: theGame.start();
15: }
16:
17: public void pauseApp()
18: {
19:
20: }
21:
22: public void destroyApp(boolean unconditional)
23: {
24:
25: }
26: }

Now the MainGame class.

1: import java.io.*;
2: import java.util.*;
3: import javax.microedition.lcdui.*;
4: import javax.microedition.lcdui.game.*;
5:
6: public class MainGame extends GameCanvas implements Runnable
7: {
8: // Links to the Images we load
9: private Image tableImage;
10: private Image paddleImage;
11: private Image ballImage;
12: private LayerManager manager;
13: private Sprite tableSprite;
14: private Sprite paddleSprite;
15: private Sprite ballSprite;
16: private Thread gameThread;
17: private boolean isGameRunning;
18:
19: public MainGame()
20: {
21: super(true);
22: loadImages();
23: manager = new LayerManager();
24: loadSprites();
25: isGameRunning = true;
26: }
27:
28: public void start()
29: {
30: gameThread = new Thread(this);
31: gameThread.run();
32: }
33: public void run()
34: {
35: Graphics g = getGraphics();
36: while(isGameRunning)
37: {
38: checkUserInput();
39: moveObjects();
40: collisionDetection();
41: updateGameScreen(g);
42: flushGraphics();
43: try
44: {
45: gameThread.sleep(60);
46: }
47: catch(InterruptedException ie)
48: {
49: System.err.println(ie);
50: }
51: }
52: }
53:
54: public void loadImages()
55: {
56: try
57: {
58: //Remember to load the res folder into netbeans
59: tableImage = Image.createImage("/PongTable.png");
60: paddleImage = Image.createImage("/Paddle.png");
61: ballImage = Image.createImage("/PongBall.png");
62: System.out.println("Loaded Images!");
63: }
64: catch(IOException ioex)
65: {
66: System.err.println(ioex+" Error loading images");
67: }
68: }
69:
70: public void loadSprites()
71: {
72: tableSprite = new Sprite(tableImage);
73: paddleSprite = new Sprite(paddleImage, 10, 50);
74: ballSprite = new Sprite(ballImage);
75:
76: manager.append(ballSprite);
77: manager.append(paddleSprite);
78: manager.append(tableSprite);
79: }
80:
81: public void checkUserInput()
82: {
83:
84: }
85:
86: public void moveObjects()
87: {
88:
89: }
90:
91: public void collisionDetection()
92: {
93:
94: }
95:
96: public void updateGameScreen(Graphics g)
97: {
98: manager.paint(g, 0, 0);
99: }
100: }

Hopefully you have the game showing something similar to what my screenshot shows. Now having a static screen like this isn’t very exciting so we want to move the ball around the screen without user input and we want the paddle to move with user input. In order to do this and make it look at least semi-professional, we should put the paddle in the middle of the “goal” and the ball on the centre circle. Since these are starting positions and will never need to be set more than once we can set them in the constructor. So, to continue with good coding practice, we will make a method that sets them up and put the code there. If you can think ahead just a little bit, we are going to do collision detection on both the paddle and the ball so we are going to want to capture the coordinates of the X and Y position of both the ball and the paddle. In order to do this we will need to add 4 new variables to the growing variable declaration list where we added the Images, Sprites, the Thread and the isGameRunning declaration. So, add the following

So, in the constructor put the following call to the method “setInitialPositions” with public access and a void return type.
Your constructor should now look like this

1: public MainGame()
2: {
3: super(true);
4: loadImages();
5: manager = new LayerManager();
6: loadSprites();
7: isGameRunning = true;
8: setInitialPositions();
9: }

The only change should be on line 8 where you add a call to “setInitialPositions”. Next, under the last method you created which should have been “public void updateGameScreen(Graphics g)”, you want to create that “setInitalPositions()” method. I will write in the code we will use, type it in and then I will explain it.

1: public void setInitialPositions()
2: {
3: //Remember the size of the sprite and the ref pixel is top left
4: paddleSprite.setRefPixelPosition(paddleSprite.getWidth(), tableSprite.getHeight() / 2 - (paddleSprite.getHeight() / 2));
5: paddleX = paddleSprite.getRefPixelX();
6: paddleY = paddleSprite.getRefPixelY();
7:
8: ballSprite.setRefPixelPosition(tableSprite.getWidth() /2 - (ballSprite.getWidth() / 2), tableSprite.getHeight() / 2 - (ballSprite.getWidth() / 2));
9: ballX = ballSprite.getRefPixelX();
10: }

Let’s walk through it line by line. Line 4 is the first thing that needs explanation. You have a link to the paddle called paddleSprite (look at your loadSprite method in case you forgot). By calling the “setRefPixelPosition” method from the paddle’s sprite class we can set where it is position on the screen (in this case only initially as we will move the paddle up and down later with input from the keypad). The method takes two parameters which are the X and Y coordinates of the new position (if you are not really understanding all that, highlight the “setRefPixelPosition” and press ALT + F1 to bring up the javadoc.). This will the position of the paddle to be re-located to the X and Y coordinates of the parameters that are passed through the method. Let’s look at what we are passing. Sadly, this is the part where Math kicks in and since some people have trouble with numbers (including myself may I add) and a certain person has had two bottles of wine (yeah, that would be me)! This may be the worst part of the tutorial but please please please struggle through it. 2D coordinates are easy to understand when you think about it long enough!
paddleSprite.setRefPixelPosition(paddleSprite.getWidth(), tableSprite.getHeight() / 2 - (paddleSprite.getHeight() / 2));
Okay, look at the first part of the line. “paddleSprite.setRePixel”. This is us saying “this is where we want the paddle to originally be positioned”. We want it smack-bang in the middle of the table and a little bit away from the left hand edge. Just remember that! Now, the X coordinate goes left to right and the Y coordinate goes from top to bottom. So, the X coordinate we want a little bit away from the left hand edge. Let’s just make it the size of a “paddle” away from the left hand edge. So we say “paddleSprite.getWidth()” which will be 10 (look at your loadSprite() method if you don’t believe me). So this says set it 10 pixels to the right of the edge. The next part of the code says “tableSprite.getWidth() / 2 - (paddleSprite.getHeight() /2 ))” Okay, it looks complicated but lets look at it logically and sequentially.
“tableSprite.getWidth() / 2” is easy it’s whatever the table width is but halved. Who cares what screen size we have, the table is of “n” size so whatever size it is, half it! Great, we have our X coordinate. Now we need the Y coordinate. The Y coordinate needs to be halfway down the screen. What you need to remember is that the way the phone measures the sprite is from a top-left coordinate of the sprite. That means we need half the height of the Sprite to be subtracted from the table’s height otherwise it will be slightly closer to the top than the bottom. So, for the Y coordinate we say “tableSprite.getHeight() / 2 - (paddleSprite.getHeight() /2))” this subtracts half the paddle height from the middle of the table height and makes the paddle show up in the middle. If it didn’t make sense, read it again. I’m sure it will this time.
We set paddleX and paddleY to the appropriate positions by coding
paddleX = paddleSprite.getRefPixelX();
paddleY = paddleSprite.getRefPixelY();
We will use this later for both collision detection and checking user input.
Next, we do the exact same for the ball but place it in the middle of the table.
ballSprite.setRefPixelPosition(tableSprite.getWidth() /2 - (ballSprite.getWidth() / 2), tableSprite.getHeight() / 2 - (ballSprite.getWidth() / 2));
Remember, the reference pixel is at the top left of the ball so we need to subtract half the ball’s width and half the ball’s height in order to get it dead centre on the screen.
By now you should have the paddle sitting in the middle of the goals and the ball in the centre. Remember when we created the paddle sprite and we created it slightly differently from the other two. What we said was
paddleSprite = new Sprite(10, 50);
Well, if you get the chance, open up the paddle image in paint or Photoshop and look at it. Notice that the first colour is a light green and is followed by 7 other colours? Well these are the different frames we are going to animate. The two values that we passed into the Sprite constructor (10, 50) is the width and height of each frame. Because I created the images, I already knew this, if you were creating your own they can technically be any size you want. The whole image was loaded into the sprite but only the first frame (10 pixels by 50 pixels) is ever drawn. If we want another of the frames to be drawn, all we have to do is add one line of code.
We are going to draw each frame sequentially. It is possible to setup a custom frame sequence but I suggest you look at the javadoc if you wish to that (it is very easy though). So, in the main game loop, just after we call the “flushGraphics()” method we want to add this

1: public void run()
2: {
3: Graphics g = getGraphics();
4: while(isGameRunning)
5: {
6: checkUserInput();
7: moveObjects();
8: collisionDetection();
9: updateGameScreen(g);
10: flushGraphics();
11: paddleSprite.nextFrame();
12: try
13: {
14: gameThread.sleep(60);
15: }
16: catch(InterruptedException ie)
17: {
18: System.err.println(ie);
19: }
20: }
21: }

The only difference is line 11. Run the code now and you should see the paddle changing colours! That’s how you animate a sprite! We have put that in the main game loop so it will continue to do that while the game is running, you could of course map it to a key press or an event just as easily.
Now, let’s get the paddle responding to user input. We already have an empty method for this called “checkUserInput()” so put this code in and we will go through it line by line.

1: public void checkUserInput()
2: {
3: int button = getKeyStates();
4: if((button & UP_PRESSED) != 0)
5: {
6: //Check the paddle is not already at the top
7: if(paddleY >0)
8: {
9: paddleY -= 4;
10: }
11: }
12: if((button & DOWN_PRESSED) != 0)
13: {
14: //Check the paddle isn't at the bottom of the screen
15: if(paddleY < tableSprite.getHeight() - paddleSprite.getHeight())
16: {
17: paddleY += 4;
18: }
19: }
20: //Set the new location
21: paddleSprite.setRefPixelPosition(paddleX, paddleY);
22: }

When you press a get on the keypad, it gets a numerical value. To access these values we link an integer to it in line 3.
Then we use if statements to determine whether a button has been pressed. The UP_PRESSED is a static field and there are plenty more that you can use (check the javadoc) for all the different buttons on the phone.
We check to see if the value does not equal zero (which means the key is pressed) and then we have a nested if after that because we would only want to move the paddle up if it was not already at the top of the screen. We then set paddleY to 4 less than it was. Remember the Y-axis starts at the top left to the bottom left.
We exit both those if statements and immediately start a new one, this time to check if the down button has been pressed (line 12). Again, we want to check that the paddle is not at the bottom of the table (not the bottom of the screen as they are two different sizes). We need to subtract the height of the paddle as well since the reference pixel is located at the top left of the paddle. Right at the end of the method, we call a method of paddleSprite that we have already used when we initially setup the position, the “setRefPixelPosition(paddleX, paddleY)”. We give it the x and y coordinates of where the paddle now is.
Run your code and press up and down either on your keyboard or on the phone’s game pad and you should see the paddle moving.
Just to keep you up to date, this is the full code listing so far
The MobilePong MIDlet

1: import javax.microedition.midlet.*;
2: import javax.microedition.lcdui.*;
3:
4: public class MobilePong extends MIDlet
5: {
6: private Display myDisplay;
7: private MainGame theGame;
8:
9: public void startApp()
10: {
11: myDisplay = Display.getDisplay(this);
12: theGame = new MainGame();
13: myDisplay.setCurrent(theGame);
14: theGame.start();
15: }
16:
17: public void pauseApp()
18: {
19:
20: }
21:
22: public void destroyApp(boolean unconditional)
23: {
24:
25: }
26: }

The MainGame class

1: import java.io.*;
2: import java.util.*;
3: import javax.microedition.lcdui.*;
4: import javax.microedition.lcdui.game.*;
5:
6: public class MainGame extends GameCanvas implements Runnable
7: {
8: // Links to the Images we load
9: private Image tableImage;
10: private Image paddleImage;
11: private Image ballImage;
12: private LayerManager manager;
13: private Sprite tableSprite;
14: private Sprite paddleSprite;
15: private Sprite ballSprite;
16: private Thread gameThread;
17: private boolean isGameRunning;
18:
19: private int paddleX;
20: private int paddleY;
22:
23:
24: public MainGame()
25: {
26: super(true);
27: loadImages();
28: manager = new LayerManager();
29: loadSprites();
30: isGameRunning = true;
31: setInitialPositions();
32: }
33:
34: public void start()
35: {
36: gameThread = new Thread(this);
37: gameThread.run();
38: }
39: public void run()
40: {
41: Graphics g = getGraphics();
42: while(isGameRunning)
43: {
44: checkUserInput();
45: moveObjects();
46: collisionDetection();
47: updateGameScreen(g);
48: flushGraphics();
49: paddleSprite.nextFrame();
50: try
51: {
52: gameThread.sleep(60);
53: }
54: catch(InterruptedException ie)
55: {
56: System.err.println(ie);
57: }
58: }
59: }
60:
61: public void loadImages()
62: {
63: try
64: {
65: //Remember to load the res folder into netbeans
66: tableImage = Image.createImage("/PongTable.png");
67: paddleImage = Image.createImage("/Paddle.png");
68: ballImage = Image.createImage("/PongBall.png");
69: System.out.println("Loaded Images!");
70: }
71: catch(IOException ioex)
72: {
73: System.err.println(ioex+" Error loading images");
74: }
75: }
76:
77: public void loadSprites()
78: {
79: tableSprite = new Sprite(tableImage);
80: paddleSprite = new Sprite(paddleImage, 10, 50);
81: ballSprite = new Sprite(ballImage);
82:
83: manager.append(ballSprite);
84: manager.append(paddleSprite);
85: manager.append(tableSprite);
86: }
87:
88: public void checkUserInput()
89: {
90: int button = getKeyStates();
91: if((button & UP_PRESSED) != 0)
92: {
93: //Check the paddle is not already at the top
94: if(paddleY >0)
95: {
96: paddleY -= 4;
97: }
98: }
99: if((button & DOWN_PRESSED) != 0)
100: {
101: //Check the paddle isn't at the bottom of the screen
102: if(paddleY < tableSprite.getHeight() - paddleSprite.getHeight())
103: {
104: paddleY += 4;
105: }
106: }
107: //Set the new location
108: paddleSprite.setRefPixelPosition(paddleX, paddleY);
109: }
110:
111: public void moveObjects()
112: {
113:
114: }
115:
116: public void collisionDetection()
117: {
118:
119: }
120:
121: public void updateGameScreen(Graphics g)
122: {
123: manager.paint(g, 0, 0);
124: }
125:
126: public void setInitialPositions()
127: {
128: //Remember the size of the sprite and the ref pixel is top left
129: paddleSprite.setRefPixelPosition(paddleSprite.getWidth(), tableSprite.getHeight() / 2 - (paddleSprite.getHeight() / 2));
130: paddleX = paddleSprite.getRefPixelX();
131: paddleY = paddleSprite.getRefPixelY();
132:
133: ballSprite.setRefPixelPosition(tableSprite.getWidth() /2 - (ballSprite.getWidth() / 2), tableSprite.getHeight() / 2 - (ballSprite.getWidth() / 2));
134: ballX = ballSprite.getRefPixelX();
135: ballY = ballSprite.getRefPixelY();
136: }
137: }

Okay, now we are going to move the ball and all the code for that will be written in the “moveObjects” method.
Now let’s think about what we are going to need in order to make the ball bounce around the screen. We are going to want to know what vertical direction it is moving in (either up or down) and the horizontal direction (either left or right). We are also going to need to update the balls position on the screen like we did with the paddle so we need the x and y coordinates of the ball at each frame. We shall start with coding the ball going up and down the screen and we need three variables to hold the information we are going to use. In the variable declarations part, just under the “private int paddleX” and “private int paddleY” we are going to put the following

1: private int paddleY;
2: private int ballX;
3: private int ballY;
4: private boolean isMovingDown;
5:
6: public MainGame()
7: {
8: super(true);
9: isMovingDown = true;
10: ....
11: }

Notice the additions at line 2, 3 and 9. We declare three variables, two of type int and one boolean and we set the boolean to true in our constructor. Now to the moveObjects method. Type in the following then I’ll give an explanation

1: public void moveObjects()
2: {
3: if(isMovingDown)
4: {
5: if(ballY < tableSprite.getHeight() - ballSprite.getHeight())
6: {
7: ballY += 3;
8: }
9: else
10: {
11: isMovingDown = false;
12: }
13: }
14: if(!isMovingDown)
15: {
16: if(ballY < tableSprite.getHeight() && ballY > 0)
17: {
18: ballY -= 3;
19: }
20: else if(ballY <= 0)
21: {
22: isMovingDown = true;
23: }
24: }
25: ballSprite.setRefPixelPosition(ballX, ballY);
26: }

First we say if the ball is moving down (which initially it is as we set the variable to true in the constructor), then we check to see if the Y Coordinate of the ball is not off the table yet. Again, the x, y coordinates for the ball are top left which is why we coded
if(ballY < tableSprite.getHeight() - ballSprite.getHeight())
If both these condition are true, we move the ball down 3.
The else statement means that the ball is at the bottom of the table and now we want the ball to go back up so we set isMovingDown to false.
Next set of if statements. First we ask if the ball is NOT moving down, i.e should it be moving up. Then we say if the Y coordinate of the ball is less than the height of the table AND is not at the top of the screen THEN we move it up 3
The last thing we do is tell the phone where the x and y coordinates of the ballSprite is by calline the “setRefPixelPosition(ballX, ballY)” method.
Now we want to allow the ball to go left and right so lets add a boolean variable called “isMovingRight” and set it to true in the constructor just like this

1: private int ballY;
2: private boolean isMovingDown;
3: private boolean isMovingRight;
4:
5: public MainGame()
6: {
7: super(true);
8: isMovingDown = true;
9: isMovingRight = true;
10: ....
11: }

Note the additions on line 3 and line 9 into your code. Now back to the moveObject() method and add the following code at the very top of the method.

1: if(isMovingRight)
2: {
3: if(ballX < tableSprite.getWidth() - ballSprite.getWidth())
4: {
5: ballX += 3;
6: }
7:
8: if(ballX >= tableSprite.getWidth() - ballSprite.getWidth())
9: {
10: isMovingRight = false;
11: }
12: }
13: else if(!isMovingRight) //If its supposed to move left
14: {
15: if(ballX > 0)
16: {
17: ballX -= 3;
18: }
19:
20: if(ballX <= 0)
21: {
22: isMovingRight = true;
23: }
24: }
25:}

This is very similar to the code for moving up and down so I will not explain it.
The full code listing at this point is
MobilePong MIDlet

1: /*
2: * To change this template, choose Tools | Templates
3: * and open the template in the editor.
4: */
5:
6: import javax.microedition.midlet.*;
7: import javax.microedition.lcdui.*;
8:
9: /**
10: * @author Chris McCready
11: */
12: public class MobilePong extends MIDlet
13: {
14: private Display myDisplay;
15: private MainGame theGame;
16:
17: public void startApp()
18: {
19: myDisplay = Display.getDisplay(this);
20: theGame = new MainGame();
21: myDisplay.setCurrent(theGame);
22: theGame.start();
23: }
24:
25: public void pauseApp()
26: {
27:
28: }
29:
30: public void destroyApp(boolean unconditional)
31: {
32:
33: }
34: }

MainGame class

1: /*
2: * To change this template, choose Tools | Templates
3: * and open the template in the editor.
4: */
5: import java.io.*;
6: import java.util.*;
7: import javax.microedition.lcdui.*;
8: import javax.microedition.lcdui.game.*;
9:
10: public class MainGame extends GameCanvas implements Runnable
11: {
12: // Links to the Images we load
13: private Image tableImage;
14: private Image paddleImage;
15: private Image ballImage;
16: private LayerManager manager;
17: private Sprite tableSprite;
18: private Sprite paddleSprite;
19: private Sprite ballSprite;
20: private Thread gameThread;
21: private boolean isGameRunning;
22:
23: private int paddleX;
24: private int paddleY;
25: private int ballX;
26: private int ballY;
27: private boolean isMovingDown;
28: private boolean isMovingRight;
29:
30: public MainGame()
31: {
32: super(true);
33: isMovingDown = true;
34: isMovingRight = true;
35: loadImages();
36: manager = new LayerManager();
37: loadSprites();
38: isGameRunning = true;
39: setInitialPositions();
40: }
41:
42: public void start()
43: {
44: gameThread = new Thread(this);
45: gameThread.run();
46: }
47: public void run()
48: {
49: Graphics g = getGraphics();
50: while(isGameRunning)
51: {
52: checkUserInput();
53: moveObjects();
54: collisionDetection();
55: updateGameScreen(g);
56: flushGraphics();
57: paddleSprite.nextFrame();
58: try
59: {
60: gameThread.sleep(60);
61: }
62: catch(InterruptedException ie)
63: {
64: System.err.println(ie);
65: }
66: }
67: }
68:
69: public void loadImages()
70: {
71: try
72: {
73: //Remember to load the res folder into netbeans
74: tableImage = Image.createImage("/PongTable.png");
75: paddleImage = Image.createImage("/Paddle.png");
76: ballImage = Image.createImage("/PongBall.png");
77: System.out.println("Loaded Images!");
78: }
79: catch(IOException ioex)
80: {
81: System.err.println(ioex+" Error loading images");
82: }
83: }
84:
85: public void loadSprites()
86: {
87: tableSprite = new Sprite(tableImage);
88: paddleSprite = new Sprite(paddleImage, 10, 50);
89: ballSprite = new Sprite(ballImage);
90:
91: manager.append(ballSprite);
92: manager.append(paddleSprite);
93: manager.append(tableSprite);
94: }
95:
96: public void checkUserInput()
97: {
98: int button = getKeyStates();
99: if((button & UP_PRESSED) != 0)
100: {
101: //Check the paddle is not already at the top
102: if(paddleY >0)
103: {
104: paddleY -= 4;
105: }
106: }
107: if((button & DOWN_PRESSED) != 0)
108: {
109: //Check the paddle isn't at the bottom of the screen
110: if(paddleY < tableSprite.getHeight() - paddleSprite.getHeight())
111: {
112: paddleY += 4;
113: }
114: }
115: //Set the new location
116: paddleSprite.setRefPixelPosition(paddleX, paddleY);
117: }
118:
119: public void moveObjects()
120: {
121: if(isMovingRight)
122: {
123: if(ballX < tableSprite.getWidth() - ballSprite.getWidth())
124: {
125: ballX += 3;
126: }
127:
128: if(ballX >= tableSprite.getWidth() - ballSprite.getWidth())
129: {
130: isMovingRight = false;
131: }
132: }
133: else if(!isMovingRight) //If its supposed to move left
134: {
135: if(ballX > 0)
136: {
137: ballX -= 3;
138: }
139:
140: if(ballX <= 0)
141: {
142: isMovingRight = true;
143: }
144: }
145:
146: if(isMovingDown)
147: {
148: if(ballY < tableSprite.getHeight() - ballSprite.getHeight())
149: {
150: ballY += 3;
151: }
152: else
153: {
154: isMovingDown = false;
155: }
156: }
157: if(!isMovingDown)
158: {
159: if(ballY < tableSprite.getHeight() && ballY > 0)
160: {
161: ballY -= 3;
162: }
163: else
164: {
165:
166: isMovingDown = true;
167: }
168: }
169: ballSprite.setRefPixelPosition(ballX, ballY);
170: }
171:
172: public void collisionDetection()
173: {
174:
175: }
176:
177: public void updateGameScreen(Graphics g)
178: {
179: manager.paint(g, 0, 0);
180: }
181:
182: public void setInitialPositions()
183: {
184: //Remember the size of the sprite and the ref pixel is top left
185: paddleSprite.setRefPixelPosition(paddleSprite.getWidth(), tableSprite.getHeight() / 2 - (paddleSprite.getHeight() / 2));
186: paddleX = paddleSprite.getRefPixelX();
187: paddleY = paddleSprite.getRefPixelY();
188:
189: ballSprite.setRefPixelPosition(tableSprite.getWidth() /2 - (ballSprite.getWidth() / 2), tableSprite.getHeight() / 2 - (ballSprite.getWidth() / 2));
190: ballX = ballSprite.getRefPixelX();
191: ballY = ballSprite.getRefPixelY();
192: }
193: }

Now we just need to do collision detection and you have a fully working (if rather basic) game.
Go to your collisionDetection() method and type in the following


1: public void collisionDetection()
2: {
3: if(ballSprite.collidesWith(paddleSprite, false))
4: {
5: isMovingRight = true;
6: }
7: }

All sprites have a method that can be called named “collidesWith()”. This method take two parameters, the first is what you want to check the collision with. In our case we check if the ball has collided with our paddle. The second parameter is a boolean value and is used if you want pixel perfect collision. We set this to false as pixel perfect collision detection is well outside the scope of this tutorial. All you need to know is that when you set it to false it will put a boundary box around the two objects and check if they are ever in the same x, y location which works fine for our simple game.
If they ever do hit, we simply tell the isMovingRight to move right as if reacting to hitting the paddle.
Run the code and move your paddle to a position that will cause a collision and what the ball bounce off the paddle!
There is only one last thing to do. Since we need to have an objective of the game (this objective being don’t let the ball hit the left hand wall) we need to change the code slightly in the moveObjects() method and add a gameOver method to our midlet.
We add all control methods (such as starting, pausing, unpausing and closing) in the midlet. So add this to the bottom of our MIDlet class.

public void gameOver()
{
destroyApp(true);
notifyDestroyed();
}

Now we need to be able to call this method from the MainGame class so, to accomplish this, we pass a reference to our MIDlet to the MainGame class. Look at this code in your startApp method (still in the MIDlet).

1: public void startApp()
2: {
3: myDisplay = Display.getDisplay(this);
4: theGame = new MainGame(this);
5: myDisplay.setCurrent(theGame);
6: theGame.start();
7: }

The only addition here is to line 4. Now we pass a reference to “this” to the MainGame constructor. Again, “this” is just a fancy way of saying “this class” which in this case is our MIDlet.
All we need to do now is change our MainGame constructor slightly after we have added a variable to link it in our variable declarations

1: private boolean isMovingRight;
2: MobilePong theMidlet;
3:
4: public MainGame(MobilePong m)
5: {
6: super(true);
7: theMidlet = m;
8: isMovingDown = true;
9: ....
10: }

Again, the only changes are on line 2 where we say we want a variable called theMidlet and it will be of type MobilePong (which is the name of your MIDlet).
We also added “MobilePong m” to the parameters on line 4 which will accept the “this” that we pass through in our midlet and on line 7 we simply say that we want our variable “theMidlet” to equal “m”.
One more step and we’re finished. We want to call the gameOver method when the ball hits the left hand wall. We have already coded it to bounce back so move to the moveObjects method and look for this

1: else if(!isMovingRight) //If its supposed to move left
2: {
3: if(ballX > 0)
4: {
5: ballX -= 3;
6: }
7:
8: if(ballX <= 0)
9: {
10: isMovingRight = true;
11: }
12: }

All we need to do is change line 10 to

theMidlet.gameOver();
isGameRunning = false;

Make sure you set the “isGameRunning” variable to false othewise the while loop that controls the game loop will not end.
That’s it! Well done, if you made it this far congratulations. Just to finish off this is how your final code should look.
PongGame MIDlet


1: import javax.microedition.midlet.*;
2: import javax.microedition.lcdui.*;
3:
4: public class MobilePong extends MIDlet
5: {
6: private Display myDisplay;
7: private MainGame theGame;
8:
9: public void startApp()
10: {
11: myDisplay = Display.getDisplay(this);
12: theGame = new MainGame(this);
13: myDisplay.setCurrent(theGame);
14: theGame.start();
15: }
16:
17: public void pauseApp()
18: {
19:
20: }
21:
22: public void destroyApp(boolean unconditional)
23: {
24: System.out.println("In destroyApp");
25: }
26:
27: public void gameOver()
28: {
29: destroyApp(true);
30: notifyDestroyed();
31: }
32: }

MainGame class

1: /*
2: * To change this template, choose Tools | Templates
3: * and open the template in the editor.
4: */
5: import java.io.*;
6: import java.util.*;
7: import javax.microedition.lcdui.*;
8: import javax.microedition.lcdui.game.*;
9:
10: public class MainGame extends GameCanvas implements Runnable
11: {
12: // Links to the Images we load
13: private Image tableImage;
14: private Image paddleImage;
15: private Image ballImage;
16: private LayerManager manager;
17: private Sprite tableSprite;
18: private Sprite paddleSprite;
19: private Sprite ballSprite;
20: private Thread gameThread;
21: private boolean isGameRunning;
22:
23: private int paddleX;
24: private int paddleY;
25: private int ballX;
26: private int ballY;
27: private boolean isMovingDown;
28: private boolean isMovingRight;
29: MobilePong theMidlet;
30:
31: public MainGame(MobilePong m)
32: {
33: super(true);
34: theMidlet = m;
35: isMovingDown = true;
36: isMovingRight = true;
37: loadImages();
38: manager = new LayerManager();
39: loadSprites();
40: isGameRunning = true;
41: setInitialPositions();
42: }
43:
44: public void start()
45: {
46: gameThread = new Thread(this);
47: gameThread.run();
48: }
49: public void run()
50: {
51: Graphics g = getGraphics();
52: while(isGameRunning)
53: {
54: checkUserInput();
55: moveObjects();
56: collisionDetection();
57: updateGameScreen(g);
58: flushGraphics();
59: paddleSprite.nextFrame();
60: try
61: {
62: gameThread.sleep(60);
63: }
64: catch(InterruptedException ie)
65: {
66: System.err.println(ie);
67: }
68: }
69: }
70:
71: public void loadImages()
72: {
73: try
74: {
75: //Remember to load the res folder into netbeans
76: tableImage = Image.createImage("/PongTable.png");
77: paddleImage = Image.createImage("/Paddle.png");
78: ballImage = Image.createImage("/PongBall.png");
79: System.out.println("Loaded Images!");
80: }
81: catch(IOException ioex)
82: {
83: System.err.println(ioex+" Error loading images");
84: }
85: }
86:
87: public void loadSprites()
88: {
89: tableSprite = new Sprite(tableImage);
90: paddleSprite = new Sprite(paddleImage, 10, 50);
91: ballSprite = new Sprite(ballImage);
92:
93: manager.append(ballSprite);
94: manager.append(paddleSprite);
95: manager.append(tableSprite);
96: }
97:
98: public void checkUserInput()
99: {
100: int button = getKeyStates();
101: if((button & UP_PRESSED) != 0)
102: {
103: //Check the paddle is not already at the top
104: if(paddleY >0)
105: {
106: paddleY -= 4;
107: }
108: }
109: if((button & DOWN_PRESSED) != 0)
110: {
111: //Check the paddle isn't at the bottom of the screen
112: if(paddleY < tableSprite.getHeight() - paddleSprite.getHeight())
113: {
114: paddleY += 4;
115: }
116: }
117: //Set the new location
118: paddleSprite.setRefPixelPosition(paddleX, paddleY);
119: }
120:
121: public void moveObjects()
122: {
123: if(isMovingRight)
124: {
125: if(ballX < tableSprite.getWidth() - ballSprite.getWidth())
126: {
127: ballX += 3;
128: }
129:
130: if(ballX >= tableSprite.getWidth() - ballSprite.getWidth())
131: {
132: isMovingRight = false;
133: }
134: }
135: else if(!isMovingRight) //If its supposed to move left
136: {
137: if(ballX > 0)
138: {
139: ballX -= 3;
140: }
141:
142: if(ballX <= 0)
143: {
144: theMidlet.gameOver();
145: isGameRunning = false;
146: }
147: }
148:
149: if(isMovingDown)
150: {
151: if(ballY < tableSprite.getHeight() - ballSprite.getHeight())
152: {
153: ballY += 3;
154: }
155: else
156: {
157: isMovingDown = false;
158: }
159: }
160: if(!isMovingDown)
161: {
162: if(ballY < tableSprite.getHeight() && ballY > 0)
163: {
164: ballY -= 3;
165: }
166: else
167: {
168:
169: isMovingDown = true;
170: }
171: }
172: ballSprite.setRefPixelPosition(ballX, ballY);
173: }
174:
175: public void collisionDetection()
176: {
177: if(ballSprite.collidesWith(paddleSprite, false))
178: {
179: isMovingRight = true;
180: }
181: }
182:
183: public void updateGameScreen(Graphics g)
184: {
185: manager.paint(g, 0, 0);
186: }
187:
188: public void setInitialPositions()
189: {
190: //Remember the size of the sprite and the ref pixel is top left
191: paddleSprite.setRefPixelPosition(paddleSprite.getWidth(), tableSprite.getHeight() / 2 - (paddleSprite.getHeight() / 2));
192: paddleX = paddleSprite.getRefPixelX();
193: paddleY = paddleSprite.getRefPixelY();
194:
195: ballSprite.setRefPixelPosition(tableSprite.getWidth() /2 - (ballSprite.getWidth() / 2), tableSprite.getHeight() / 2 - (ballSprite.getWidth() / 2));
196: ballX = ballSprite.getRefPixelX();
197: ballY = ballSprite.getRefPixelY();
198: }
199: }

So that’s it. It really is that easy.
On a final note, I would appreciate any feedback, comments and criticism. I am always looking for ways to improve both the tutorial and the actual code so if you did read this tutorial, it would be greatly appreciated if you would leave a comment on this blog letting me know how you found it.
If you want to play around some more with this code I have a few suggestions you may want to try on your own to show off your new found understanding.
1) Sprites can be rotated and mirrored using the “setTransform()” method. Look up the javadoc to see how it works but it is really simple. The only thing you have to keep in mind is that it is transformed around it reference pixel (which as you should all know, since I have written it about 100 times, is the top left). You can change the reference pixel to be anywhere so it would be advantageous to look at how transforms work.
2) Create a new class that extends the GameCanvas and create your own image in your favourite drawing package. Use that to display a game over screen rather than just a straight exit.
3) Similar to the above. Create a splash screen that extends the GameCanvas that shows the name of the game, instructions and your name (cause you wrote it, you wanna tell as many people as possible)! Display this first and load the game once you have finished loading all the appropriate classes and methods. Keep in mind that since we have an exceedingly simple game, you may want to put it on a timer as it only take about half a second to set up the constructor on this class!
Happy Coding, I hope to hear your feedback!
Chris

6 comments:

Anonymous said...

That's a cool tutorial man. I've always assumed games programming was hard to do. But that tutorial was pretty good and easy to follow. (which is good going after 2 bottles of wine!)

Now to try figure out some way of getting another paddle in there and getting some sort of AI on the go.... (possibility for a follow up tutorial?) :-)

Unknown said...

Well done, Chris. Found that very useful and a few things cleared up.. Thanks. Andy

Anonymous said...

Very good tutorial, takes you through step by step, and with good clear explanations.

thanks

Anonymous said...

very good tuto, each line of code is easy to understand with these explainations. Thank you

Anonymous said...

Hey chris, tutorial was great. got it working etc now :D

there's a few things missing accidentally i think which i've emailed you if you're interested :)

overall great though, I think the structure of it was perfect in terms of teaching understanding of the principles behind the game.

dario said...

why do you not put the code complete for dowload?