|
On this page I will try to optimize the game playing experience. I hope you did not expect code optimizations :-)
We are going to add a score board as well as increase the speed of the game, when the user is ahead of the AI in score.
To accomplish this we must add variables to hold the score (padScore and AI score), and these must be updated when the ball hits the border on either side of the screen. In addition to this, we must adjust the sleepTime variable (which controls the speed of the game) according to the current score. This is accomplished in the final listing of the game.
package dk.koderko.games.pong;
import java.io.IOException;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.Sprite;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import java.util.Random;
public class PongCanvas extends GameCanvas implements Runnable {
public PongCanvas() {
super(false);
}
public void run() {
while(true) {
updateScreen(getGraphics());
try {
Thread.sleep(sleepTime);
} catch (Exception e) {
}
}
}
public void start() {
try {
ballImg = Image.createImage("/ball.png");
padImg = Image.createImage("/pad.png");
} catch (IOException ioex) {
System.out.println(ioex);
}
ballSprite = new Sprite(ballImg, 3, 3);
ballSprite.defineReferencePixel(2, 2);
ballSprite.setRefPixelPosition(ballX, ballY);
padSprite = new Sprite(padImg, 3, 20);
padSprite.defineReferencePixel(1, 10);
padSprite.setRefPixelPosition(padX, padY);
AISprite = new Sprite(padImg, 3, 20);
AISprite.defineReferencePixel(3, 10);
AISprite.setRefPixelPosition(AIX, AIY);
Thread runner = new Thread(this);
runner.start();
}
private void createBackground(Graphics g) {
g.setColor(0x000000);
g.fillRect(0, 0, getWidth(), getHeight());
}
private void updateScreen(Graphics g) {
createBackground(g);
createScoreboard(g); // Added
moveBall();
movePad();
moveAI();
ballSprite.setRefPixelPosition(ballX, ballY);
ballSprite.paint(g);
padSprite.setRefPixelPosition(padX, padY);
padSprite.paint(g);
AISprite.setRefPixelPosition(AIX, AIY);
AISprite.paint(g);
flushGraphics();
}
private void moveBall() {
if (ballDirection == 0) {
ballX -= ballXVel;
ballY -= ballYVel;
} else if (ballDirection == 1) {
ballX += ballXVel;
ballY -= ballYVel;
} else if (ballDirection == 2) {
ballX += ballXVel;
ballY += ballYVel;
} else if (ballDirection == 3) {
ballX -= ballXVel;
ballY += ballYVel;
}
if (ballDirection == 0 && ballX < 0) {
ballDirection = 1;
AIScore++; // Added
} else if (ballDirection == 0 && ballY < 0) {
ballDirection = 3;
} else if (ballDirection == 1 && ballY < 0) {
ballDirection = 2;
} else if (ballDirection == 1 && ballX > getWidth()) {
ballDirection = 0;
padScore++; // Added
if (sleepTime > 5) sleepTime--;
} else if (ballDirection == 2 && ballY > getHeight()) {
ballDirection = 1;
} else if (ballDirection == 2 && ballX > getWidth()) {
ballDirection = 3;
padScore++; // Added
if (sleepTime > 5) sleepTime--;
} else if (ballDirection == 3 && ballY > getHeight()) {
ballDirection = 0;
} else if (ballDirection == 3 && ballX < 0) {
ballDirection = 2;
AIScore++; // Added
}
if (ballDirection == 0 && ballSprite.collidesWith(padSprite, false)) {
ballDirection = 1;
} else if (ballDirection == 3 && ballSprite.collidesWith(padSprite, false)) {
ballDirection = 2;
} else if (ballDirection == 1 && ballSprite.collidesWith(AISprite, false)) {
ballDirection = 0;
} else if (ballDirection == 2 && ballSprite.collidesWith(AISprite, false)) {
ballDirection = 3;
}
sleepTime += AIScore - padScore; // Added
if (sleepTime < 5) sleepTime = 5; // Added
}
private void movePad() {
int keyState = getKeyStates();
if ((keyState & UP_PRESSED) != 0 && padY > padSprite.getHeight() / 2) {
padY -= padYVel;
} else if ((keyState & DOWN_PRESSED) != 0 && padY <= getHeight() - padSprite.getHeight() / 2) {
padY += padYVel;
}
}
private void moveAI() {
Random random = new Random();
actX = getWidth() / 3 + Math.abs(random.nextInt() % (getWidth() / 8));
if (ballY < AIY && ballX > actX && AIY > AISprite.getHeight() / 2) AIY -= AIYVel;
if (ballY > AIY && ballX > actX && AIY < getHeight() - AISprite.getHeight() / 2) AIY += AIYVel;
}
private void createScoreboard(Graphics g) { // Added entirely new method
g.setColor(0xffffff);
g.drawString(padScore + " - " + AIScore, getWidth() / 2, 20, Graphics.HCENTER | Graphics.TOP);
}
private int sleepTime = 30;
private Image ballImg;
private Sprite ballSprite;
private int ballX = getWidth() / 2;
private int ballY = getHeight() / 2;
private final static int ballXVel = 3;
private final static int ballYVel = 1;
private int ballDirection = 1;
private Image padImg;
private Sprite padSprite;
private int padX = 10;
private int padY = getHeight() / 2;
private final static int padYVel = 2;
private Sprite AISprite;
private int AIX = getWidth() - 10;
private int AIY = getHeight() / 2;
private final static int AIYVel = 2;
private int actX;
private int padScore = 0; // Added
private int AIScore = 0; // Added
}
For the last time I'll go through each addition.
createScoreboard(g);
Naturally we need to call the new createScoreboard() method from the updateScreen() method, to make sure it is continuously updated. If you have forgotten what "g" did, have a look at the background page.
if (ballDirection == 0 && ballX < 0) {
ballDirection = 1;
AIScore++; // Added
} else if (ballDirection == 0 && ballY < 0) {
ballDirection = 3;
} else if (ballDirection == 1 && ballY < 0) {
ballDirection = 2;
} else if (ballDirection == 1 && ballX > getWidth()) {
ballDirection = 0;
padScore++; // Added
if (sleepTime > 5) sleepTime--;
} else if (ballDirection == 2 && ballY > getHeight()) {
ballDirection = 1;
} else if (ballDirection == 2 && ballX > getWidth()) {
ballDirection = 3;
padScore++; // Added
if (sleepTime > 5) sleepTime--;
} else if (ballDirection == 3 && ballY > getHeight()) {
ballDirection = 0;
} else if (ballDirection == 3 && ballX < 0) {
ballDirection = 2;
AIScore++; // Added
}
In this collision detection block, we must increment the score whenever the ball hits the border on either the left or right side of the screen. If it hits on the left side (ballX < 0) it means that the user did not catch the ball, so the AIScore must be incremented. If it hits on the right side of the screen (ballX > getWidth()), it means the AI did not catch the ball, so the padScore is incremented.
sleepTime += AIScore - padScore;
if (sleepTime < 5) sleepTime = 5;
These two lines make the speed increase, when the player gets ahead of the AI. The higher the difference between the players score (padScore) and the AIScore, the faster the game runs (since sleepTime is reduced). There is a limit though: When the sleepTime reaches 5 milliseconds, it is not possible to go faster.
private void createScoreboard(Graphics g) {
g.setColor(0xffffff);
g.drawString(padScore + " - " + AIScore, getWidth() / 2, 20, Graphics.HCENTER | Graphics.TOP);
}
This is a new method. First we set the color to be used to white (0xffffff), and then we "draw" a string on the screen. The parameters are as follows: drawString(text to be drawn, position on x-axis, position on y-axis, alignment). In this case it draws "padScore - AIScore", in the middle of the screen on the x-axis (getWidth() / 2) and 20px from the top when we use Graphics.HCENTER and Graphics.TOP.
| |
| |
More Information
To get more information about Graphics.HCENTER, Graphics.TOP, please refer to this page. |
|
| |
private int padScore = 0;
private int AIScore = 0;
Finally, we have to initialize the two score variables.
The Result
And with these "optimizations" our game is done! Hoped you learned a few things on the way. Feedback is very much appreciated over here, or via email (see the bottom of the page).
Your Own Improvements
The game could be improved in a number of ways. Ie. you could look into these areas: 1) The velocity of the ball, the pad and the AI 2) The graphics, you could add more fancy graphics or background images 3) The "AI" could be improved to act faster or slower by changing the Random parameters 4) You could add obstacles on the play field. There is plenty of improvement options available.
|
|