So I have been watching some videos and reading some articles recently on Double Buffering and Threads and I tried to incorporate them into my program. My program is to make Duck Hunt. I have most of the program done except I need to make the ducks bounce around the screen. I tried to do this with a thread with some double buffering but my ducks just stand still. I'm using a parent class for the ducks and then a child class for each different colored duck.
The Main Class makes the frame and paints the background.
The thread is created in the paint method of the Levels Class.
The double buffering occurs in the Duck classes.
This is my main class:
import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Image; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.ImageIcon; import javax.swing.JFrame; /** * This class is the main, base class. It sets the JFrame, adds/implements MouseListener, * creates a background image, and creates a back, play, instruction and quit button. * * It also creates an object of the ScoreDisplay class and an object of the Levels class. */ public class MainStructure extends JFrame implements MouseListener { /** * Holds the background image */ static Image background; /** * Play, Instructions, Quit and back buttons */ private Buttons playButton, instructButton, exitButton, backButton; /** * Booleans to determine which screen should be showing: * (Menu, Instructions, or Game play) */ private boolean menu = true, instructions = false, gamePlay = false; /** * Object of ScoreDisplay to keep track of scores, bullets */ private ScoreDisplay scoreTrack; /** * Object of Levels to keep track of the levels/round and ducks. */ private Levels level; /** * Main method creates a MainStructure object */ public static void main (String[] args) { new MainStructure(); } /** * The Menu constructor calls setup() method, sets the Title, Size, and * visibility of the frame. */ public MainStructure() { setup(); setTitle("Java Game"); setSize(713,539); setResizable(false); setVisible(true); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } /** * The setup() method adds a MouseListner, sets the background image, creates the * play, quit, instructions, and back buttons. Those first three buttons will * be available from the main menu and the back button will be available from the * instruction page to go back to the main menu. * * Then, this method make a new ScoreDisplay object and a new Levels object. */ public void setup() { addMouseListener(this); ImageIcon backPic = new ImageIcon("C:/Users/Bob/Documents/School/Spring2012/CSC211/DuckHunt/src/DuckHuntBackGround.jpg"); background = backPic.getImage(); playButton = new Buttons("Play", Color.white, 275, 100, 150, 50 ); instructButton = new Buttons("Instructions", Color.white, 275, 175, 150, 50 ); exitButton = new Buttons("Quit Game", Color.white, 275, 250, 150, 50 ); backButton = new Buttons("Back", Color.black, 31, 477, 74, 51); scoreTrack = new ScoreDisplay(); level = new Levels(); } /** * The paint method paints the background first. Then it checks to see * which of three booleans is true. Each boolean corresponds to a different * state of the program: Menu-page, Instructions-page, and Game play-page. * * If it is on the menu page, it will paint the play, instructions and quit buttons. * If it is on the instructions page, it will call the instructions method. * If it is on the game play page, it will paint the Levels and ScoreDisplay object. */ public void paint(Graphics pane) { pane.drawImage(background, 0, 0, this); if (menu == true) { playButton.paint(pane); instructButton.paint(pane); exitButton.paint(pane); } if (instructions == true) { instructions(pane); } if (gamePlay == true) { scoreTrack.paint(pane); level.paint(pane); } } /** * * This instructions method paints a white box and then assigns * each lines of the instructions to a space in an array. * * After that, it goes through the array and paints each string * of the array. * * It also paints the back button. */ public void instructions(Graphics pane) { pane.setColor(Color.white); pane.fillRect(250, 50, 250, 280); pane.setColor(Color.black); pane.drawRect(250, 50, 250, 280); String[]lines = new String [11]; lines[0] = "For each wave of ducks, you have three "; lines[1] = "bullets to use and a certain amount of " ; lines[2] = "time to shoot the duck before it flies and " ; lines[3] = "goes off screen. Run out of ammo and "; lines[4] = "they go away immediately."; lines[5] = ""; lines[6] = "You are required to shoot a certain amount "; lines[7] = "of targets per round. See that blue bar "; lines[8] = "beneath the target indicator, if the "; lines[9] = "number of targets hit matches or pass the "; lines[10] = "bar, then you proceed."; pane.drawString("Instructions", 340, 80); int lineY = 110; for(int i = 0; i<lines.length; i++) { pane.drawString(lines[i], 265, lineY); lineY += 20; } backButton.paint(pane); } /** * The mouseClicked method gets the x and y of the users click. * With this, it checks to see if the click is within any of the * buttons. If it is is, it changes the appropriate booleans * to change the page of the game (menu, instructions, game play) * accordingly. */ public void mouseClicked(MouseEvent event) { /* * Gets the X and Y coordinates of the mouse click. */ int x = event.getX(); int y = event.getY(); System.out.println(x); System.out.println(y); /* * If play button is pressed: */ if(playButton.isInside(x, y)) { if (menu == true) { menu = false; gamePlay = true; repaint(); } } if(instructButton.isInside(x, y)) { if (menu == true) { instructions = true; menu = false; repaint(); } } if(exitButton.isInside(x, y)) { if (menu == true) { System.exit(0); } } if(backButton.isInside(x, y)) { if (instructions == true) { instructions = false; menu = true; repaint(); } } } public void mouseEntered(MouseEvent event) {} public void mouseExited(MouseEvent event) {} /** * The mousePressed method, checks to see if the game * is playing and if so checks to see if shot hit a duck. * * If it did hit the duck then it calls the bulletReset() * method from the ScoreDisplay object and adds points to * the user's score. */ public void mousePressed(MouseEvent event) { int x = event.getX(); int y = event.getY(); boolean duckHit = false; if(gamePlay == true ) { scoreTrack.bulletMinus(); duckHit = level.hitCheck(x,y); if(duckHit==true) { scoreTrack.bulletReset(); } repaint(); } } public void mouseReleased(MouseEvent event) {} }
This is my Levels Class:
import java.awt.Color; import java.awt.Graphics; /** * This class creates the duck objects and keeps track of the * level. It resets the duck array every round to a harder * combination of ducks. */ public class Levels{ /** * What round/wave of ducks it is. */ private int round; /** * Which duck of the round the user is currently * seeing/shooting at. */ private int duckCount; /** * How many black ducks of the round. */ private int blackCount; /** * How many blue ducks of the round. */ private int blueCount; /** * How many red ducks of the round. */ private int redCount; /** * An array of 10 ducks. These ducks are an assortment * of the correct number of red/blue/black ducks * of the round. */ private BaseDuck[] duckArray = new BaseDuck[10]; /** * To keep track of the current array index when * assigning ducks to the array. */ private int arrayIndex; /** * Constructor calls setup() method. */ public Levels() { setup(); } /** * Sets round to 1 and sets up initial duck array. */ public void setup() { round = 1; setDucks(); } /** * Called by the hitCheck method. If the duck is hit or time is up * for the 10th duck, the user will go to the next round. The * counts will be set back to zero and setDucks will be called again * to set the new combination of blue/red/black ducks in the array. */ public void nextRound() { round++; duckCount = 0; arrayIndex = 0; setDucks(); } /** * Paints the current round number and paints the calls the current * duck object in the array to be painted. */ public void paint(Graphics pane) { String roundString = "Round: " + Integer.toString(round); pane.fillRect(35, 444, 65, 21); pane.setColor(Color.white); pane.drawString(roundString, 40, 460); duckArray[duckCount].paint(pane); Thread t1 = new Thread(duckArray[duckCount]); t1.start(); } /** * Checks the round and sets the correct number * of black/blue/red ducks to their Count variables. * * Then it fills the array with the correct number * of each colored duck. */ public void setDucks() { if (round==1) { blackCount = 6; blueCount = 2; redCount = 2; } if (round==2) { blackCount = 5; blueCount = 3; redCount = 2; } if (round==3) { blackCount = 4; blueCount = 4; redCount = 2; } if (round==4) { blackCount = 5; blueCount = 2; redCount = 3; } if (round==5) { blackCount = 4; blueCount = 3; redCount = 3; } if (round==6) { blackCount = 3; blueCount = 4; redCount = 3; } if (round==7) { blackCount = 4; blueCount = 2; redCount = 4; } if (round==8) { blackCount = 3; blueCount = 3; redCount = 4; } if (round==9) { blackCount = 2; blueCount = 2; redCount = 4; } if (round==10) { blackCount = 1; blueCount = 5; redCount = 4; } if (round==11) { blackCount = 0; blueCount = 6; redCount = 4; } if (round==12) { blackCount = 1; blueCount = 4; redCount = 5; } if (round==13) { blackCount = 0; blueCount = 5; redCount = 5; } if (round==14) { blackCount = 2; blueCount = 2; redCount = 6; } if (round==15) { blackCount = 1; blueCount = 3; redCount = 6; } if (round==16) { blackCount = 0; blueCount = 4; redCount = 6; } if (round==17) { blackCount = 1; blueCount = 2; redCount = 7; } if (round==18) { blackCount = 0; blueCount = 2; redCount = 8; } if (round==19) { blackCount = 0; blueCount = 1; redCount = 9; } if (round==20) { blackCount = 0; blueCount = 0; redCount = 10; } for(int i = 0; i<blackCount; i++) { duckArray[arrayIndex]= new BlackDuck(); arrayIndex++; } for(int i = 0; i<blueCount; i++) { duckArray[arrayIndex]= new BlueDuck(); arrayIndex++; } for(int i = 0; i<redCount; i++) { duckArray[arrayIndex]= new RedDuck(); arrayIndex++; } } /** * Called from MainStructure to see if the current * duck was hit or not. If so the next duck appears * and if it was the 10th duck of the round, it calls * the nextRound method. * * Returns boolean of whether duck was hit or not. * f * @param someX X-coordinate of user's click * @param someY Y-Coordinate of user's click * @return */ public boolean hitCheck(int someX, int someY) { boolean hit = false; if(duckArray[duckCount].isInside(someX, someY)) { hit = true; System.out.println("HIT"); duckCount++; if(duckCount == 10) { nextRound(); } } return hit; } }
This is my parent class for the duck:
import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Polygon; import java.awt.Shape; import javax.swing.JFrame; import javax.swing.JPanel; /** * This is the parent Duck class. */ public abstract class BaseDuck extends JPanel implements Runnable { protected Image dbImage; protected Graphics dbg; protected int x, y, xDirection, yDirection; /** * Shape of the duck. */ protected Shape duckShape; /** * Color of the duck. */ protected Color duckColor; /** * Constructor calls setShape. */ public BaseDuck() { x=0; y=0; setXDirection(1); setYDirection(1); setShape(); } /** * setColor to be override by derived classes. */ public abstract void setColor(); /** * setSpeed to be override by derived classes. */ public abstract void setSpeed(); /** * Sets the shape of the duck with a polygon and * multiple points. */ public void setShape() { Polygon poly = new Polygon(); poly.addPoint(x+516,y+259); poly.addPoint(x+461,y+267); poly.addPoint(x+485,y+245); poly.addPoint(x+488,y+242); poly.addPoint(x+485,y+245); poly.addPoint(x+483,y+241); poly.addPoint(x+479,y+232); poly.addPoint(x+478,y+226); poly.addPoint(x+478,y+218); poly.addPoint(x+488,y+213); poly.addPoint(x+494,y+212); poly.addPoint(x+514,y+226); poly.addPoint(x+541,y+213); poly.addPoint(x+533,y+251); poly.addPoint(x+514,y+226); poly.addPoint(x+529,y+246); poly.addPoint(x+536,y+255); poly.addPoint(x+531,y+265); poly.addPoint(x+525,y+266); poly.addPoint(x+518,y+268); poly.addPoint(x+514,y+268); poly.addPoint(x+508,y+263); poly.addPoint(x+507,y+260); duckShape = poly; } /** * Called by the Levels object to see if bullet is inside * the bound of the shape of the duck. * @param pointX -user's x-coordinate of click * @param pointY -user's y-coordinate of click * @return -boolean of if hit or not */ public boolean isInside(int pointX, int pointY) { return duckShape.contains(pointX,pointY); } /** * Sets up the double buffering so there is not any choppyness * in duck movement. */ public void paint(Graphics pane) { dbImage = createImage(getWidth(), getHeight()); dbg = dbImage.getGraphics(); paintComponent(dbg); pane.drawImage(dbImage, 0, 0, this); System.out.println("HI"); } /** * Sets appropriate color for the duck and * then it paints the duck shape. */ public void paintComponent(Graphics pane) { Graphics2D pane2 = (Graphics2D)pane; pane2.setColor(duckColor); pane2.fill(duckShape); pane2.setColor(Color.black); pane2.draw(duckShape); } /** * Runs the duck movement by calling the move() method * with a thread delay. */ public void run() { System.out.println("HI"); try { while(true) { move(); Thread.sleep(5); } } catch(Exception e) { System.out.println("Error"); } } /** * Sets the x and y direction of the duck. If the duck hits * the wall it changes the direction making it bounce. */ public void move() { x += xDirection; y += yDirection; if(x<=0) { setXDirection(1); } if(x>=500) { setXDirection(-1); } if(y<=0) { setYDirection(1); } if(y>=500) { setYDirection(-1); } repaint(); } /** * Called by move() method, to change X Direction */ public void setXDirection(int xdir) { xDirection = xdir; } /** * Called by move() method, to change Y Direction */ public void setYDirection(int ydir) { yDirection = ydir; } }
And one of the duck child classes (There are two more; one for black, one for blue, which are the same except for the color)
import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; public class RedDuck extends BaseDuck { /** * Speed modifier for different difficulties * of ducks. */ private int speedMod; protected int x, y, xDirection, yDirection; /** * Color of duck since color will vary on * which difficulty duck. */ private Color duckColor; /** * Constructor calls super(), setColor() and setSpeed(). */ public RedDuck() { super(); x=0; y=0; setXDirection(1); setYDirection(1); setColor(); setSpeed(); } /** * Sets color to red. */ public void setColor() { duckColor = Color.red; } /** * Sets the speed to fastest. */ public void setSpeed() { } /** * Overridden paint method that paints the duck * with the color from setColor. */ public void paintComponent(Graphics pane) { Graphics2D pane2 = (Graphics2D)pane; pane2.setColor(duckColor); pane2.fill(duckShape); pane2.setColor(Color.black); pane2.draw(duckShape); repaint(); } }
So I try to make a thread in the levels class in the paint method to move the duck but the duck just stays still. Any idea what I did wrong?