In this lesson, we will clean up some of the code in the classes, and we will be conceptually discussing collision detection, on which we will spend the next few lessons starting on Day 4.
It will be a very short lesson, as the meat of the discussion will be the collision detection basics that follow.
I'm glad you are keeping up with the series, and I hope this lesson will be interesting!
The first thing we will do will be some housekeeping. If you would rather skip all this, skip the cleaning steps and you will find the edited Robot, StartingClass, and Tiles classes below.
Cleaning up our Robot Class
Most of these changes will deal with our Robot class. So let's open that up.
When this class was created, we assumed that the ground would always be at a certain level. We will now be changing that.
1. Remove the following lines of code highlighted in RED
2. Make the changes as shown in GREEN
Next, we want to do something about that shooting mechanism. Rather than let the player hold the button to shoot, we want him to repress the button each time.
1. For that, we simply create a variable in the class declaration (add the following in Bold):
private boolean movingLeft = false;
private boolean movingRight = false;
private boolean ducked = false;
private boolean readyToFire = true;
2. We want this new boolean to be accessible from other classes, so we right click >> source >> Generate Getters and Setters. Make sure that "readyToFire" is checked, and press OK.
You can manually do this by adding:
public boolean isReadyToFire() {
return readyToFire;
}
public void setReadyToFire(boolean readyToFire) {
this.readyToFire = readyToFire;
}
Wherever you feel appropriate.
3. Now scroll down to the shoot() method, and surround the two statements around an if statement like so:
public void shoot() {
if (readyToFire) {
Projectile p = new Projectile(centerX + 50, centerY - 25);
projectiles.add(p);
}
}
This will ensure that only when the boolean "readyToFire" is true, the player will be able to shoot.
4. Now, open up StartingClass. Navigate down to the keyPressed() method. For case KeyEvent.VK_CONTROL, you want to set it so that each time that robot.shoot() is called, readyToFire will be set to false.
It will look like this:
case KeyEvent.VK_CONTROL:
if (robot.isDucked() == false && robot.isJumped() == false) {
robot.shoot();
robot.setReadyToFire(false);
}
break;
5. Lastly, scroll further down to the keyReleased() method. Add the following case (and statement).
case KeyEvent.VK_CONTROL:
robot.setReadyToFire(true);
break;
With all of this, you should be able to run the game again, and this time holding down the Control button will not shoot endlessly.
CLEANING UP OUR StartingCLASS
1. Scroll up to where we first declare private Robot robot;
We will be making robot a static variable that can be accessed by other classes (for collision detection purposes). So add the static keyword after private.
2. We want to create a Getter method for the robot. So scroll down to the bottom (below getBg2) and add the following:
public static Robot getRobot(){
return robot;
}
3. Lastly, we want to make a small change to the start() method.
Locate the following line: robot = new Robot();
Move it directly below this one: bg2 = new Background(2160, 0);
Now we can call getRobot() as soon as the tiles are initialized.
Cleaning Up our Tiles Class
1. Locate the update() method and change it to look like this one:
public void update() {
speedX = bg.getSpeedX() * 5;
tileX += speedX;
}
2. Secondly, above where we create the bg object, add this line:
private Robot robot = StartingClass.getRobot();
The Resulting Classes
Robot Class
import java.util.ArrayList;
public class Robot {
// Constants are Here
final int JUMPSPEED = -15;
final int MOVESPEED = 5;
private int centerX = 100;
private int centerY = 377;
private boolean jumped = false;
private boolean movingLeft = false;
private boolean movingRight = false;
private boolean ducked = false;
private boolean readyToFire = true;
private int speedX = 0;
private int speedY = 0;
private Background bg1 = StartingClass.getBg1();
private Background bg2 = StartingClass.getBg2();
private ArrayList<Projectile> projectiles = new ArrayList<Projectile>();
public void update() {
// Moves Character or Scrolls Background accordingly.
if (speedX < 0) {
centerX += speedX;
}
if (speedX == 0 || speedX < 0) {
bg1.setSpeedX(0);
bg2.setSpeedX(0);
}
if (centerX <= 200 && speedX > 0) {
centerX += speedX;
}
if (speedX > 0 && centerX > 200) {
bg1.setSpeedX(-MOVESPEED / 5);
bg2.setSpeedX(-MOVESPEED / 5);
}
// Updates Y Position
centerY += speedY;
// Handles Jumping
if (jumped == true) {
speedY += 1;
}
// Prevents going beyond X coordinate of 0
if (centerX + speedX <= 60) {
centerX = 61;
}
}
public boolean isReadyToFire() {
return readyToFire;
}
public void setReadyToFire(boolean readyToFire) {
this.readyToFire = readyToFire;
}
public void moveRight() {
if (ducked == false) {
speedX = MOVESPEED;
}
}
public void moveLeft() {
if (ducked == false) {
speedX = -MOVESPEED;
}
}
public void stopRight() {
setMovingRight(false);
stop();
}
public void stopLeft() {
setMovingLeft(false);
stop();
}
private void stop() {
if (isMovingRight() == false && isMovingLeft() == false) {
speedX = 0;
}
if (isMovingRight() == false && isMovingLeft() == true) {
moveLeft();
}
if (isMovingRight() == true && isMovingLeft() == false) {
moveRight();
}
}
public void jump() {
if (jumped == false) {
speedY = JUMPSPEED;
jumped = true;
}
}
public void shoot() {
if (readyToFire) {
Projectile p = new Projectile(centerX + 50, centerY - 25);
projectiles.add(p);
}
}
public int getCenterX() {
return centerX;
}
public int getCenterY() {
return centerY;
}
public boolean isJumped() {
return jumped;
}
public int getSpeedX() {
return speedX;
}
public int getSpeedY() {
return speedY;
}
public void setCenterX(int centerX) {
this.centerX = centerX;
}
public void setCenterY(int centerY) {
this.centerY = centerY;
}
public void setJumped(boolean jumped) {
this.jumped = jumped;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
public void setSpeedY(int speedY) {
this.speedY = speedY;
}
public boolean isDucked() {
return ducked;
}
public void setDucked(boolean ducked) {
this.ducked = ducked;
}
public boolean isMovingRight() {
return movingRight;
}
public void setMovingRight(boolean movingRight) {
this.movingRight = movingRight;
}
public boolean isMovingLeft() {
return movingLeft;
}
public void setMovingLeft(boolean movingLeft) {
this.movingLeft = movingLeft;
}
public ArrayList getProjectiles() {
return projectiles;
}
}
StartingClass
import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import kiloboltgame.framework.Animation;
public class StartingClass extends Applet implements Runnable, KeyListener {
private static Robot robot;
private Heliboy hb, hb2;
private Image image, currentSprite, character, character2, character3,
characterDown, characterJumped, background, heliboy, heliboy2,
heliboy3, heliboy4, heliboy5;
public static Image tilegrassTop, tilegrassBot, tilegrassLeft, tilegrassRight, tiledirt;
private Graphics second;
private URL base;
private static Background bg1, bg2;
private Animation anim, hanim;
private ArrayList<Tile> tilearray = new ArrayList<Tile>();
@Override
public void init() {
setSize(800, 480);
setBackground(Color.BLACK);
setFocusable(true);
addKeyListener(this);
Frame frame = (Frame) this.getParent().getParent();
frame.setTitle("Q-Bot Alpha");
try {
base = getDocumentBase();
} catch (Exception e) {
// TODO: handle exception
}
// Image Setups
character = getImage(base, "data/character.png");
character2 = getImage(base, "data/character2.png");
character3 = getImage(base, "data/character3.png");
characterDown = getImage(base, "data/down.png");
characterJumped = getImage(base, "data/jumped.png");
heliboy = getImage(base, "data/heliboy.png");
heliboy2 = getImage(base, "data/heliboy2.png");
heliboy3 = getImage(base, "data/heliboy3.png");
heliboy4 = getImage(base, "data/heliboy4.png");
heliboy5 = getImage(base, "data/heliboy5.png");
background = getImage(base, "data/background.png");
tiledirt = getImage(base, "data/tiledirt.png");
tilegrassTop = getImage(base, "data/tilegrasstop.png");
tilegrassBot = getImage(base, "data/tilegrassbot.png");
tilegrassLeft = getImage(base, "data/tilegrassleft.png");
tilegrassRight = getImage(base, "data/tilegrassright.png");
anim = new Animation();
anim.addFrame(character, 1250);
anim.addFrame(character2, 50);
anim.addFrame(character3, 50);
anim.addFrame(character2, 50);
hanim = new Animation();
hanim.addFrame(heliboy, 100);
hanim.addFrame(heliboy2, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy5, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy2, 100);
currentSprite = anim.getImage();
}
@Override
public void start() {
bg1 = new Background(0, 0);
bg2 = new Background(2160, 0);
robot = new Robot();
// Initialize Tiles
try {
loadMap("data/map1.txt");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
hb = new Heliboy(340, 360);
hb2 = new Heliboy(700, 360);
Thread thread = new Thread(this);
thread.start();
}
private void loadMap(String filename) throws IOException {
ArrayList lines = new ArrayList();
int width = 0;
int height = 0;
BufferedReader reader = new BufferedReader(new FileReader(filename));
while (true) {
String line = reader.readLine();
// no more lines to read
if (line == null) {
reader.close();
break;
}
if (!line.startsWith("!")) {
lines.add(line);
width = Math.max(width, line.length());
}
}
height = lines.size();
for (int j = 0; j < 12; j++) {
String line = (String) lines.get(j);
for (int i = 0; i < width; i++) {
if (i < line.length()) {
char ch = line.charAt(i);
Tile t = new Tile(i, j, Character.getNumericValue(ch));
tilearray.add(t);
}
}
}
}
@Override
public void stop() {
// TODO Auto-generated method stub
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void run() {
while (true) {
robot.update();
if (robot.isJumped()) {
currentSprite = characterJumped;
} else if (robot.isJumped() == false && robot.isDucked() == false) {
currentSprite = anim.getImage();
}
ArrayList projectiles = robot.getProjectiles();
for (int i = 0; i < projectiles.size(); i++) {
Projectile p = (Projectile) projectiles.get(i);
if (p.isVisible() == true) {
p.update();
} else {
projectiles.remove(i);
}
}
updateTiles();
hb.update();
hb2.update();
bg1.update();
bg2.update();
animate();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void animate() {
anim.update(10);
hanim.update(50);
}
@Override
public void update(Graphics g) {
if (image == null) {
image = createImage(this.getWidth(), this.getHeight());
second = image.getGraphics();
}
second.setColor(getBackground());
second.fillRect(0, 0, getWidth(), getHeight());
second.setColor(getForeground());
paint(second);
g.drawImage(image, 0, 0, this);
}
@Override
public void paint(Graphics g) {
g.drawImage(background, bg1.getBgX(), bg1.getBgY(), this);
g.drawImage(background, bg2.getBgX(), bg2.getBgY(), this);
paintTiles(g);
ArrayList projectiles = robot.getProjectiles();
for (int i = 0; i < projectiles.size(); i++) {
Projectile p = (Projectile) projectiles.get(i);
g.setColor(Color.YELLOW);
g.fillRect(p.getX(), p.getY(), 10, 5);
}
g.drawImage(currentSprite, robot.getCenterX() - 61,
robot.getCenterY() - 63, this);
g.drawImage(hanim.getImage(), hb.getCenterX() - 48,
hb.getCenterY() - 48, this);
g.drawImage(hanim.getImage(), hb2.getCenterX() - 48,
hb2.getCenterY() - 48, this);
}
private void updateTiles() {
for (int i = 0; i < tilearray.size(); i++) {
Tile t = (Tile) tilearray.get(i);
t.update();
}
}
private void paintTiles(Graphics g) {
for (int i = 0; i < tilearray.size(); i++) {
Tile t = (Tile) tilearray.get(i);
g.drawImage(t.getTileImage(), t.getTileX(), t.getTileY(), this);
}
}
@Override
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
System.out.println("Move up");
break;
case KeyEvent.VK_DOWN:
currentSprite = characterDown;
if (robot.isJumped() == false) {
robot.setDucked(true);
robot.setSpeedX(0);
}
break;
case KeyEvent.VK_LEFT:
robot.moveLeft();
robot.setMovingLeft(true);
break;
case KeyEvent.VK_RIGHT:
robot.moveRight();
robot.setMovingRight(true);
break;
case KeyEvent.VK_SPACE:
robot.jump();
break;
case KeyEvent.VK_CONTROL:
if (robot.isDucked() == false && robot.isJumped() == false) {
robot.shoot();
}
break;
}
}
@Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
System.out.println("Stop moving up");
break;
case KeyEvent.VK_DOWN:
currentSprite = anim.getImage();
robot.setDucked(false);
break;
case KeyEvent.VK_LEFT:
robot.stopLeft();
break;
case KeyEvent.VK_RIGHT:
robot.stopRight();
break;
case KeyEvent.VK_SPACE:
break;
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
public static Background getBg1() {
return bg1;
}
public static Background getBg2() {
return bg2;
}
public static Robot getRobot(){
return robot;
}
}
Tile Class
import java.awt.Image;
public class Tile {
private int tileX, tileY, speedX, type;
public Image tileImage;
private Robot robot = StartingClass.getRobot();
private Background bg = StartingClass.getBg1();
public Tile(int x, int y, int typeInt) {
tileX = x * 40;
tileY = y * 40;
type = typeInt;
if (type == 5) {
tileImage = StartingClass.tiledirt;
} else if (type == 8) {
tileImage = StartingClass.tilegrassTop;
} else if (type == 4) {
tileImage = StartingClass.tilegrassLeft;
} else if (type == 6) {
tileImage = StartingClass.tilegrassRight;
} else if (type == 2) {
tileImage = StartingClass.tilegrassBot;
}
}
public void update() {
speedX = bg.getSpeedX() * 5;
tileX += speedX;
}
public int getTileX() {
return tileX;
}
public void setTileX(int tileX) {
this.tileX = tileX;
}
public int getTileY() {
return tileY;
}
public void setTileY(int tileY) {
this.tileY = tileY;
}
public Image getTileImage() {
return tileImage;
}
public void setTileImage(Image tileImage) {
this.tileImage = tileImage;
}
}
Click on the button below!