I'm learning Java and this is what I've been able to come up with.
Please take a look at my code and tell me about the wrong approaches I've employed
(Apart from the lack of comments).
Also:
--tell me the good habits I should keep-up with.
--What does line 163 do?
--How do I make the keyboard control the player (My **keyPressed()** method doesn't work)
There are five Classes: Is it OO enough?
Class 1:
Class 2:
Class 3:
Class 4:
Class 5:
Please take a look at my code and tell me about the wrong approaches I've employed
(Apart from the lack of comments).
Also:
--tell me the good habits I should keep-up with.
--What does line 163 do?
--How do I make the keyboard control the player (My **keyPressed()** method doesn't work)
There are five Classes: Is it OO enough?
Class 1:
Code:
//SpaceInvaders.java
//This is the main class that runs the Applet
package space_game;
import acm.program.*;
import acm.graphics.*;
import acm.util.*;
import java.applet.AudioClip;
import java.awt.Color;
import java.awt.event.*;
import java.util.ArrayList;
public class SpaceInvaders extends GraphicsProgram{
private static final int APP_WIDTH = 1100;
private static final int APP_HEIGHT = 600;
private static final int GAME_BOARD_WIDTH = 800;
private static final int GAME_BOARD_HEIGHT = 400;
private static final double BULLET_DIM = 3;
private static final double PLAYER_WIDTH = 15;
private static final double PLAYER_HEIGHT = 5;
private static final double INVADER_WIDTH = 9;
private static final double INVADER_HEIGHT = 12;
private static final int PADDING = 5;
private static final Color BG_COLOR = Color.WHITE;
private static final Color BULLET_COLOR = Color.ORANGE;
private static final Color PLAYER_COLOR = Color.GREEN;
//private static final Color INVADER_COLOR = Color.LIGHT_GRAY;
private GameBoard board;
private GRect[] backGround;
private GRect playArea;
private Color[] boardCols;
private Player player = new Player(PLAYER_WIDTH, PLAYER_HEIGHT,PLAYER_COLOR);
private ArrayList<Bullet> bullet = new ArrayList<Bullet>();
private Invader invader[];
private int curBullet = 0;
private double totalBullets = 0.0;
private int level = 0;
private double score = 0;
private double speed;
private Invader.Type type;
private Bullet.Type bType;
private RandomGenerator rgen = new RandomGenerator();
private double yCord;
private int invadersRemovalCount = 0;
private int totalInvadersRemoved = 0;
private boolean gamePaused;
private boolean gameOver;
private AudioClip coming;
private AudioClip invaded;
private AudioClip shoot;
private AudioClip killed;
public void init(){
resize(APP_WIDTH, APP_HEIGHT);
setBackground(BG_COLOR);
addMouseListeners();
backGround = new GRect[4];
boardCols = new Color[4];
boardCols[0] = Color.BLACK;
boardCols[1] = Color.BLUE;
boardCols[2] = Color.GRAY;
boardCols[3] = Color.WHITE;
speed = 0.3;
bType = Bullet.Type.BALLER;
coming = MediaTools.loadAudioClip("audio/CrayonRock.au");
invaded = MediaTools.loadAudioClip("audio/OnTheHunt.au");
shoot = MediaTools.loadAudioClip("audio/gun.au");
killed = MediaTools.loadAudioClip("audio/tweeters.au");
gamePaused = false;
gameOver = false;
}
public void run(){
setup();
int scoreChecker;
while(true){
if(level_1()){
type = Invader.Type.PAWN;
bType = Bullet.Type.BALLER;
scoreChecker = 1;
}
else if(level_2()){
type = Invader.Type.KNIGHT;
bType = Bullet.Type.BALLER;
speed = 0.2;
scoreChecker = 2;
}
else if(level_3()){
type = Invader.Type.QUEEN;
bType = Bullet.Type.SNIPER;
speed = 0.1;
scoreChecker = 3;
}
else {
coming.stop();
coming = MediaTools.loadAudioClip("audio/BussaMove.au");
coming.loop();
break;
}
score *= scoreChecker;
board.updateInfo(1, String.valueOf((int) score));
createInvaders(new GPoint((playArea.getX()+playArea.getWidth())-PADDING, playArea.getY()+PADDING), playArea.getWidth(), playArea.getHeight()-PADDING, type, rgen.nextInt((int) (APP_HEIGHT*0.03), (int) (APP_HEIGHT*0.05)));
board.updateInfo(2, String.valueOf(level+1).concat(" / 10"));
invadersRemovalCount = 0;
moveInvaders(invader);
level++;
}
gameOver = true;
reset();
}
private void setup(){
board = new GameBoard(GAME_BOARD_WIDTH, GAME_BOARD_HEIGHT, boardCols ,">>>SP4C3 INV4D3RS<<<", PADDING, 13, "Invaders Exterminated: ", "Bullets Used: ");
add(board, (getWidth()-board.getWidth())/2, (getHeight()-board.getHeight())/2);
playArea = new GRect(board.getX(), board.getY()+(board.getHeight()*0.15), board.getWidth(), board.getPlayArea().getHeight());
backGround[0] = new GRect(APP_WIDTH, APP_HEIGHT);
backGround[0].setFilled(true);
backGround[0].setColor(boardCols[2]);
add(backGround[0], 0, 0);
backGround[1] = new GRect(APP_WIDTH, APP_HEIGHT);
backGround[1].setFilled(true);
backGround[1].setColor(getBackground());
add(backGround[1], 0, playArea.getY());
backGround[2] = new GRect(APP_WIDTH, APP_HEIGHT);
backGround[2].setFilled(true);
backGround[2].setColor(boardCols[2]);
add(backGround[2], 0, getYEnd(playArea));
add(playArea);
playArea.setVisible(false);
playArea.addMouseMotionListener(this);
board.sendToFront();
add(player, board.getX()+PADDING, (playArea.getY()+(playArea.getHeight()*0.5)));
backGround[3] = new GRect(0,0);
backGround[3].setBounds(board.getPauseButton().getBounds());
backGround[3].setLocation(playArea.getX()+((playArea.getWidth()-backGround[3].getWidth())/2), (playArea.getY()-((board.getStatusArea().getHeight()-backGround[3].getHeight())/2)-backGround[3].getHeight()));
add(backGround[3]);
//backGround[3].setVisible(false);
coming.loop();
}
private void createInvaders(GPoint pos, double wdt, double hgt, Invader.Type type, int numInvaders){
invader = new Invader[numInvaders];
for(int i=0; i<numInvaders; i++){
invader[i] = new Invader(INVADER_WIDTH, INVADER_HEIGHT, type);
add(invader[i], pos.getX()+rgen.nextDouble(0.0, wdt*0.25), pos.getY()+((hgt/numInvaders)*i));
invader[i].setVisible(false);
}
}
private void moveInvaders(Invader[] inv){
int i ;
while(true){
if(gamePaused){
board.changeState();
waitForClick();
board.changeState();
}
gamePaused = false;
if (!bullet.isEmpty())
for(int j=0; j<bullet.size(); j++){
bullet.get(j).move(2, 0);
if(collides(bullet.get(j), inv) || bullet.get(j).getX()>=getXEnd(playArea))
remove(bullet.get(j));
}
if(invadersExited(inv, invadersRemovalCount)){
invaded.play();
if(levelChanging()){
reset();
}
break;
}
for(i=0; i<inv.length; i++){
inv[i].move(-1, 0);
if(xIsInPlayArea(inv[i].getX(), inv[i].getWidth())){
inv[i].setVisible(true);
}
if(!inv[i].isDead() && inv[i].getX()<=playArea.getX()){
inv[i].changeState();
remove(inv[i]);
invadersRemovalCount++;
}
}
pause(100*speed);
animatebottomBar();
}
for(i=0; i<inv.length; i++){
remove(inv[i]);
}
}
private void animatebottomBar() {
board.name.move(-1, 0);
if(board.name.getX()<board.getPlayArea().getX())
board.name.setLocation(getXEnd(board.getPlayArea()), board.name.getY());
}
private boolean levelChanging(){
return (level==2||level==5||level==9);
}
private boolean collides(Bullet b, Invader[] inv){
for(int i =0; i<inv.length; i++){
if(!inv[i].isDead() && (inv[i].contains(b.getX()+b.getWidth()*0.9, b.getY()+b.getHeight()) || inv[i].contains(b.getX()+b.getWidth()*0.9, b.getY()+b.getHeight()))){
inv[i].hit();
if(inv[i].maxHitReached()){
inv[i].changeState();
killed.play();
remove(inv[i]);
invadersRemovalCount++;
totalInvadersRemoved++;
score = ((totalInvadersRemoved/totalBullets)*1000);
board.updateInfo(3, "Invaders Exterminated: "+totalInvadersRemoved);
board.updateInfo(1, String.valueOf((int) score));
}
return true;
}
}
return false;
}
private boolean invadersExited(Invader[] inv, int count){
return count == inv.length;
}
private boolean level_1(){
return level<3;
}
private boolean level_2(){
return level<6;
}
private boolean level_3(){
return level<10;
}
public void keyPressed(KeyEvent k){
if(k.getKeyCode() == KeyEvent.VK_ENTER)
shoot();
if(k.getKeyCode() == KeyEvent.VK_UP)
player.move(0,-1);
if(k.getKeyCode() == KeyEvent.VK_DOWN)
player.move(0,1);
shoot();
showStatus("..."+k.getKeyCode());
}
public void mouseClicked(MouseEvent e){
if(backGround[3].contains(e.getX(), e.getY())){
gamePaused = true;
//((GCompound) getElementAt(e.getX(), e.getY())).scale(50);
}
}
public void mousePressed(MouseEvent e){
if(!gameOver){
shoot();
}
}
public void mouseMoved(MouseEvent e){
if(yIsInPlayArea(player.getY(), player.getHeight())){
player.move(0, e.getY()-yCord);
yCord = e.getY();
}
if(outUpwards(player.getY())){
player.move(0, 2);
}
if(outDownwards(getYEnd(player))){
player.move(0, -2);
}
}
public void mouseEntered(MouseEvent e){
player.setLocation(player.getX(), e.getY());
}
private void shoot(){
switch(bType){
case SNIPER:
bullet.add(new Bullet(BULLET_DIM,Color.WHITE, bType));
break;
default:
bullet.add(new Bullet(BULLET_DIM,BULLET_COLOR, bType));
break;
}
add(bullet.get(curBullet), player.getX()+player.getWidth(), player.getY());
if(yIsInPlayArea((bullet.get(curBullet).getY()), bullet.get(curBullet).getHeight())){
shoot.play();
curBullet++;
totalBullets++;
score = ((totalInvadersRemoved/totalBullets)*1000);
board.updateInfo(4, "Bullets Used: ".concat(String.valueOf((int) totalBullets)));
board.updateInfo(1, String.valueOf((int) score));
}
else{
bullet.get(curBullet).sendToBack();
}
}
private boolean xIsInPlayArea(double x, double wdt){
return (playArea.getX()<=x)&&((playArea.getX()+playArea.getWidth()>=x+wdt));
}
private boolean yIsInPlayArea(double y, double hgt){
return (playArea.getY()<=y)&&((playArea.getY()+playArea.getHeight()>=y+hgt));
}
private boolean outUpwards(double y){
return playArea.getY()>y;
}
private boolean outDownwards(double y){
return playArea.getY()+playArea.getHeight()<y;
}
private double getXEnd(GObject g){
return g.getX()+g.getWidth();
}
private double getYEnd(GObject g){
return (g.getY()+g.getHeight());
}
private void reset(){
for(int j=0; j<bullet.size(); j++){
remove(bullet.get(j));
}
bullet.clear();
curBullet = 0;
board.name.setLocation((board.getPlayArea().getX()+(board.getPlayArea().getWidth()-board.name.getWidth())/2), board.name.getY());
showStatus("RESET CARRIED OUT!");
}
}
Class 2:
Code:
//Player.java creates a shooting object [a Gun]
package space_game;
import acm.graphics.*;
import java.awt.Color;
public class Player extends GCompound{
private GRect player;
public Player(double wdt, double hgt, Color col1, Color col2){
player = new GRect(wdt, hgt);
player.setFilled(true);
player.setColor(col1);
player.setFillColor(col2);
add(player, 0,0);
player = new GRect(wdt*0.4, hgt*0.7);
player.setFilled(true);
player.setColor(col1);
player.setFillColor(col2);
add(player, 0,hgt);
player = new GRect(wdt*0.2, hgt*0.5);
player.setFilled(true);
player.setColor(col1);
player.setFillColor(col2);
add(player, 0,hgt*1.7);
}
public Player(double wdt, double hgt, Color col){
this(wdt, hgt, col, col);
}
public Player(double wdt, Color col){
this(wdt,wdt,col);
}
}
Class 3:
Code:
//Bullet.java creates the bullet fired by the gun above
package space_game;
import acm.graphics.*;
import java.awt.Color;
public class Bullet extends GCompound{
private GOval bullet;
private GOval[] bulletArr = new GOval[4];
private Type type;
public Bullet(double dia, Color col){
this(dia, col, col, Type.BALLER);
}
public Bullet(double dia, Color col, Type t){
this(dia, col, col, t);
}
public Bullet(double dia, Color col1, Color col2, Type t){
type = t;
switch(type){
case BALLER:
bullet = new GOval(dia,dia);
bullet.setFilled(true);
bullet.setColor(col1);
bullet.setFillColor(col2);
add(bullet, 0 ,0);
break;
case SNIPER:
dia*=2;
for(int i = 0; i<4; i++){
bulletArr[i] = new GOval(dia*((i+1)*(0.55)),dia*((i+1)*(0.2)));
bulletArr[i].setFilled(true);
bulletArr[i].setColor(col1);
bulletArr[i].setFillColor(col2);
switch(i){
case 0:
add(bulletArr[0], 0 ,0);
break;
default:
add(bulletArr[i], bulletArr[i-1].getWidth()*0.9 , -bulletArr[i-1].getHeight()*0.5);
break;
}
}
break;
}
}
public enum Type{
BALLER, SNIPER
}
}
Class 4:
Code:
//Invader.java creates an enemy object
package space_game;
import acm.graphics.*;
import acm.util.ErrorException;
import java.awt.Color;
public class Invader extends GCompound{
private GPolygon invader;
private Color bodyColor;
private Color skinColor;
private int hits;
private Type type;
private State state;
public Invader(double wdt, double hgt, Color col1, Type t){
switch(t){
case PAWN:
type = Type.PAWN;
bodyColor = Color.PINK;
break;
case KNIGHT:
type = Type.KNIGHT;
bodyColor = Color.MAGENTA;
break;
case QUEEN:
type = Type.QUEEN;
bodyColor = Color.RED;
wdt*=1.5;
hgt*=1.5;
break;
default: throw new ErrorException("Invalid Type!");
}
hits = 0;
state = State.ALIVE;
skinColor = col1;
invader = new GPolygon();
invader.addVertex(-wdt, hgt*0.125);
invader.addEdge(wdt*0.5, hgt*0.125);
invader.addEdge(-wdt*0.5, hgt*0.125);
invader.addEdge(wdt*0.5, hgt*0.125);
invader.addEdge(-wdt*0.5, hgt*0.125);
invader.addEdge(wdt*0.5, hgt*0.125);
invader.addEdge(-wdt*0.5, hgt*0.125);
invader.addEdge(wdt, hgt*0.125);
invader.addVertex(0,0);
invader.setFilled(true);
invader.setColor(skinColor);
invader.setFillColor(bodyColor);
add(invader, 0,0);
}
public Invader(double wdt, double hgt, Type t){
this(wdt, hgt, Color.BLACK, t);
}
public Invader(double wdt, Color col, Type t){
this(wdt,wdt,Color.BLACK,t);
}
public void setType(Type t){
switch(t){
case PAWN:
bodyColor = Color.PINK;
type = Type.PAWN;
break;
case KNIGHT:
bodyColor = Color.MAGENTA;
type = Type.KNIGHT;
break;
case QUEEN:
bodyColor = Color.RED;
type = Type.QUEEN;
break;
default: throw new ErrorException("Invalid Type!");
}
}
public void hit(){
this.hits++;
}
public int getHits(){
return hits;
}
public boolean maxHitReached(){
switch (this.type){
case PAWN:
return(this.hits==2);
case KNIGHT:
return(this.hits==4);
case QUEEN:
return(this.hits==4);
default:
return false;
}
}
public void changeState(){
if(state == State.ALIVE)
state = State.DEAD;
else{
state = State.ALIVE;
}
}
public boolean isDead(){
return (state == State.DEAD);
}
public enum Type{
PAWN, KNIGHT, QUEEN
}
public enum State{
ALIVE, DEAD
}
}
Class 5:
Code:
//GameBoard.java creates a game-board that can be used by other 2d 'arcade' games
package space_game;
/**
* >>>[File: GameBoard.java]<<<
* [=======================]
*
* Creates a Game-Board Template
*
* [=======================]
*
* This is an arcade-style-GameBoard Java class.
* The Game-Board objects created from here can
* be used by various game objects from other
* classes
*
* [================================================]
*
* @author ac3Takwas
*
*/
import acm.graphics.*;
import java.awt.Color;
public class GameBoard extends GCompound{
/*Declaration of instance variables*/
private GRect statusArea;
private GRect pauseButton;
private GRoundRect playArea;
private GRoundRect bottom;
public GLabel name;
private GLabel scoreArea;
private GLabel level;
private GLabel info_1;
private GLabel info_2;
private GLabel pauseLabel;
private int padding;
/**
* Constructs a Game-Board given the listed parameters.
* @param width
* @param height
* @param col
* @param gameName
* @param padding
* @param statusFont
* @param info_1
* @param info_2
*/
public GameBoard(double width, double height, Color[] col, String gameName, int padding, int statusFont, String info_1, String info_2){
playArea = new GRoundRect(width, height*0.75);
statusArea = new GRect(playArea.getWidth()-(playArea.getArcWidth()*2), height*0.15);
bottom = new GRoundRect(playArea.getWidth(), height*0.1);
pauseLabel = new GLabel("PAUSE");
this.padding = padding;
createCanvas(height, col);
addBoardElements(col, gameName, padding, statusFont, info_1, info_2);
addPauseButton(col, width, padding, statusFont);
}
/**
* Creates the Game-Board canvas when called by a Constructor
* [========================================================]
*
* @param hgt
* @param colorPack
*/
private void createCanvas(double hgt, Color[] colorPack){
statusArea.setFilled(true);
statusArea.setFillColor(colorPack[1]);
statusArea.setColor(colorPack[0]);
playArea.setFilled(true);
playArea.setFillColor(colorPack[0]);
playArea.setColor(colorPack[1]);
bottom.setFilled(true);
bottom.setFillColor(colorPack[1]);
bottom.setColor(colorPack[0]);
add(statusArea, playArea.getArcWidth(), 0);
add(playArea, 0, hgt*0.15);
add(bottom, 0, playArea.getY()+playArea.getHeight());
}
/**
* Adds the various screen elements to the already drawn canvas.
* For instance the element that displays the game score is created
* by this method
* [===============================================================]
*
* @param colorPack
* @param gameName
* @param padding
* @param statusFont
* @param info_1
* @param info_2
*/
private void addBoardElements(Color colorPack[], String gameName, int padding, int statusFont, String info_1, String info_2) {
addTopLevelElements(colorPack, padding, statusFont, info_1, info_2);
addBottomLevelElements(colorPack, gameName);
}
/*Adds the Game-Board elements displayed at the top of the Playing-Area*/
private void addTopLevelElements(Color[] colorPack, int padding, int statusFont, String info_1, String info_2) {
scoreArea = new GLabel("SCORE: 0");
scoreArea.setFont("Courier-BOLD-"+statusFont);
scoreArea.setColor(colorPack[3]);
add(scoreArea, ((statusArea.getY()+statusArea.getWidth())-((scoreArea.getWidth())+padding)), statusArea.getY()+padding+scoreArea.getAscent());
level = new GLabel("LEVEL: 1");
level.setFont("Courier-BOLD-"+statusFont);
level.setColor(colorPack[3]);
add(level, ((statusArea.getY()+statusArea.getWidth())-((level.getWidth())+padding)), (statusArea.getY()+statusArea.getHeight())-(padding/2));
if(info_1 != null){
this.info_1 = new GLabel(""+info_1);
this.info_1.setFont("Courier-BOLD-"+statusFont);
this.info_1.setColor(colorPack[3]);
add(this.info_1, (playArea.getArcWidth())+padding, statusArea.getY()+padding+this.info_1.getAscent());
}
if(info_2 != null){
this.info_2 = new GLabel(""+info_2);
this.info_2.setFont("Courier-BOLD-"+statusFont);
this.info_2.setColor(colorPack[3]);
add(this.info_2, (playArea.getArcWidth())+padding, (statusArea.getY()+statusArea.getHeight())-(padding/2));
}
}
/*Adds the Game-Board elements displayed at the bottom of the Playing-Area*/
private void addBottomLevelElements(Color[] colorPack, String gameName) {
name = new GLabel(gameName);
name.setFont("Courier-BOLD-15");
name.setColor(colorPack[2]);
add(name, (getWidth()-name.getWidth())/2, (bottom.getY()+bottom.getHeight())-bottom.getArcHeight());
}
/**
* Creates and adds the button that pauses and resumes the game
* [==========================================================]
*
* @param col
* @param width
* @param padding
* @param statusFont
*/
private void addPauseButton(Color[] col, double width, int padding, int statusFont) {
pauseLabel.setFont("Courier-BOLD-"+statusFont);
pauseLabel.setColor(col[3]);
pauseButton = new GRect(pauseLabel.getWidth()+padding*2, pauseLabel.getAscent()+padding*2);
pauseButton.setFilled(true);
pauseButton.setFillColor(col[2]);
pauseButton.setColor(col[0]);
add(pauseButton, (getWidth()-pauseButton.getWidth())/2, (statusArea.getHeight() - pauseButton.getHeight())/2);
add(pauseLabel, pauseButton.getX()+padding, pauseButton.getY()+padding+pauseLabel.getAscent());
}
@Override
public void repaint(){
}
public boolean isInPlayArea(GObject g){
return contains(playArea, g);
}
/**
* @param g1
* @param g2
* @return true if g1 contains g2
*/
private boolean contains(GObject g1, GObject g2){
return (g1.getY()<=g2.getY())&&((g1.getY()+g1.getHeight()>=g2.getY()+g2.getHeight()))
&&
(g1.getX()<=g2.getX())&&((g1.getX()+g1.getWidth()>=g2.getX()+g2.getWidth()));
}
public boolean yIsInPlayArea(double y, double hgt){
return (this.playArea.getY()<=y)&&((this.playArea.getY()+this.playArea.getHeight()>=y+hgt));
}
public boolean xIsInPlayArea(double x, double wdt){
return (this.playArea.getX()<=x)&&((this.playArea.getX()+this.playArea.getWidth()>=x+wdt));
}
public boolean outUpwards(double y){
return this.playArea.getY()>y;
}
public boolean outDownwards(double y){
return this.playArea.getY()+this.playArea.getHeight()<y;
}
public GObject getPlayArea(){
return this.playArea;
}
public GObject getPauseButton(){
return this.pauseButton;
}
public GObject getStatusArea(){
return this.statusArea;
}
public void changeState(){
if(pauseLabel.getLabel().equalsIgnoreCase("Start")){
pauseLabel.setLabel("PAUSE");
}
else{
pauseLabel.setLabel("START");
}
repaint();
}
public void updateInfo(int index, String str){
switch(index){
case 1:
scoreArea.setLabel("SCORE: "+str);
scoreArea.setLocation((statusArea.getY()+statusArea.getWidth())-((scoreArea.getWidth())+padding), scoreArea.getY());
break;
case 2:
level.setLabel("LEVEL: "+str);
level.setLocation((statusArea.getY()+statusArea.getWidth())-((level.getWidth())+padding), level.getY());
break;
case 3:
info_1.setLabel(""+str);
break;
default:
info_2.setLabel(""+str);
break;
}
repaint();
}
}