Computer Science 252
Problem Solving with Java
Spring 2016, The College of Saint Rose
FruitNinja BlueJ Project
Click here to download a BlueJ project for FruitNinja.
FruitNinja Source Code
The Java source code for FruitNinja is below. Click on a file name to download it.
import objectdraw.*; import java.awt.*; import java.util.ArrayList; import java.util.Random; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; /* * Example FruitNinja: an example final project that implements a simplified version * of the "Fruit Ninja" game. Each time the user presses the button to launch * more flying fruit, an active object is created that in turn creates a series of * flying fruit. The player attempts to drag the mouse over each flying fruit object * to earn a point. If they do not do so before it falls off the bottom of the canvas, * a miss is recorded. When 3 misses are recorded, the game ends. * * Jim Teresco, The College of Saint Rose, CSC 252, Spring 2016 * * $Id: FruitNinja.java 2915 2016-03-24 02:23:40Z terescoj $ */ public class FruitNinja extends WindowController implements ActionListener { // how many fruits can you miss before the game ends? private static final int MAX_MISSES = 3; // instance variables to keep track of scoring private JLabel scoreLabel; private int score, misses; // the game keeps a list of FlyingFruit objects currently in the air private ArrayList<FlyingFruit> fruitList; public void begin() { setSize(600, 600); Container contentPane = getContentPane(); // A JPanel, in which we will group together our JButton // and JLabel for the score JPanel southPanel = new JPanel(); JButton nextButton = new JButton("Next Round"); southPanel.add(nextButton); scoreLabel = new JLabel("Score: 0, Misses: 0"); southPanel.add(scoreLabel); // listen for button presses nextButton.addActionListener(this); // get our panel into the pane contentPane.add(southPanel, BorderLayout.SOUTH); contentPane.validate(); // arraylist of FlyingFruit objects fruitList = new ArrayList<FlyingFruit>(); } // handle button press, only one button so it's simple public void actionPerformed(ActionEvent e) { if (misses < MAX_MISSES) { // the FruitLauncher is responsible for launching a series of // FlyingFruit objects new FruitLauncher(this, canvas); } } // method to inform the game that there's a new FlyingFruit // in the air to check in our drag public synchronized void newFruit(FlyingFruit f) { fruitList.add(f); } // method to inform the game that a piece of fruit is done, either // because the fruit was hit or it went off the canvas, as indicated // by the second parameter public synchronized void fruitDone(FlyingFruit f, boolean hit) { if (!hit) { if (misses < MAX_MISSES) { misses++; scoreLabel.setText("Score: " + score + ", Misses: " + misses); } if (misses == MAX_MISSES) { new FilledRect(0, 0, canvas.getWidth(), canvas.getHeight(), canvas).setColor(Color.red); Text gameOver = new Text("Game Over!", 0, 0, canvas); gameOver.setColor(Color.white); gameOver.setFontSize(20); gameOver.move(canvas.getWidth()/2 - gameOver.getWidth()/2, canvas.getHeight()/2 - gameOver.getHeight()/2); } } fruitList.remove(f); } // the only mouse event we care about is onMouseDrag, we simply loop over // all of the FlyingFruit objects currently in play and see if any of them // contain the point public void onMouseDrag(Location point) { if (misses < MAX_MISSES) { for (FlyingFruit f : fruitList) { if (f.contains(point)) { f.hit(); score++; } } scoreLabel.setText("Score: " + score + ", Misses: " + misses); } } }
/** * Active object to create a series of flying fruits for * Fruit Ninja game * * @author Jim Teresco */ import objectdraw.*; import java.awt.*; import java.util.Random; public class FruitLauncher extends ActiveObject { // we'll create between 3 and 7 FlyingFruits per FruitLauncher private static final int MIN_FRUITS = 3; private static final int MAX_FRUITS = 7; // in ms: private static final int AVERAGE_INTERVAL = 500; // just some information we need to remember from the parameters // to the constructor that need to be used in the run method, where // they are passed to the FlyingFruit constructor private DrawingCanvas c; private FruitNinja game; // constructor remembers a few bits of information then activates public FruitLauncher(FruitNinja game, DrawingCanvas c) { this.c = c; this.game = game; start(); } // the life our our launcher is to decide how many to launch, then launch them public void run() { Random r = new Random(); int numFruits = r.nextInt(MAX_FRUITS - MIN_FRUITS) + MIN_FRUITS; for (int fruitNum = 0; fruitNum < numFruits; fruitNum++) { new FlyingFruit(game, c); pause(AVERAGE_INTERVAL); } } }
/** * One flying piece of fruit for the Fruit Ninja game. * * @author Jim Teresco */ import objectdraw.*; import java.awt.*; import java.util.Random; public class FlyingFruit extends ActiveObject { // lots of speeds and sizes private static final double MIN_XSPEED = -2; private static final double MAX_XSPEED = 2; private static final double MIN_YSPEED = -6; private static final double MAX_YSPEED = -10; private static final double FRUIT_SIZE = 40; private static final int SHRAPNEL_COUNT = 4; private static final double GRAVITY = 0.1; private static final int PAUSE_TIME = 33; // the actual graphical object that's flying private FilledOval fruit; // our speeds private double xSpeed, ySpeed; // need to remember the canvas private DrawingCanvas c; // reference to the game's WindowController which will be used // to report when new instances are created and when instances // fall off the bottom of the canvas private FruitNinja game; // have we hit this one yet? private boolean hit = false; // the constructor is responsible for remembering some information, // choosing a starting position and speed, and telling the game // that it exists. public FlyingFruit(FruitNinja game, DrawingCanvas c) { this.game = game; this.c = c; Random r = new Random(); // random x position within middle "half" of canvas double xPos = r.nextDouble()*c.getWidth()/2 + c.getWidth()/4 - FRUIT_SIZE/2; // our actual fruit object fruit = new FilledOval(xPos, c.getHeight(), FRUIT_SIZE, FRUIT_SIZE, c); fruit.setColor(new Color(r.nextInt(200), r.nextInt(200), r.nextInt(200))); // calculate initial speed xSpeed = r.nextDouble()*(MAX_XSPEED - MIN_XSPEED) + MIN_XSPEED; ySpeed = r.nextDouble()*(MAX_YSPEED - MIN_YSPEED) + MIN_YSPEED; // tell the game that we have a new FlyingFruit game.newFruit(this); start(); } // method to see if the fruit object here contains a point public boolean contains(Location point) { return fruit.contains(point); } // method to call when this fruit object has been hit public void hit() { hit = true; } // the life of a FlyingFruit public void run() { boolean done = false; while (!done) { // move it fruit.move(xSpeed, ySpeed); // acceleration due to gravity ySpeed += GRAVITY; // wait a little pause(PAUSE_TIME); // see if we've gone off the bottom if (ySpeed > 0 && fruit.getY() > c.getHeight()) { done = true; } // if we've been hit, explode! if (hit) { done = true; // start the shrapnel here Color color = fruit.getColor(); double x = fruit.getX() + FRUIT_SIZE/2; double y = fruit.getY() + FRUIT_SIZE/2; for (int i = 0; i < SHRAPNEL_COUNT; i++) { new Shrapnel(x, y, xSpeed, ySpeed, color, c); } } } // inform the game that this object is done, hit value indicates // hit or miss so game can update status game.fruitDone(this, hit); fruit.removeFromCanvas(); } }
/** * Shrapnel of a hit fruit object, just a straightforward * animation of an object subject to acceleration due to * gravity with no other real interactions. * * @author Jim Teresco */ import objectdraw.*; import java.awt.*; import java.util.Random; public class Shrapnel extends ActiveObject { // animation parameters private static final double MAX_SPEED_CHANGE = 2; private static final double SHRAPNEL_SIZE = 20; private static final int PAUSE_TIME = 33; private static final double GRAVITY = 0.1; // instance variables for the actual graphical object, and its // animation speeds private FilledOval s; private double xSpeed, ySpeed; // we'll need this (or just its height) to decide when we've gone // off the bottom - using the canvas itself means things are still ok // if the canvas gets resized. private DrawingCanvas c; private static Random r = new Random(); // construct our shrapnel public Shrapnel(double x, double y, double xSpeed, double ySpeed, Color color, DrawingCanvas c) { this.c = c; s = new FilledOval(x - SHRAPNEL_SIZE/2, y - SHRAPNEL_SIZE/2, SHRAPNEL_SIZE, SHRAPNEL_SIZE, c); s.setColor(color); // choose a random speed similar to current speed this.xSpeed = xSpeed + r.nextDouble()*2*MAX_SPEED_CHANGE - MAX_SPEED_CHANGE; this.ySpeed = ySpeed + r.nextDouble()*2*MAX_SPEED_CHANGE - MAX_SPEED_CHANGE; start(); } // the simple life of fruit shrapnel public void run() { while (s.getY() < c.getHeight()) { s.move(xSpeed, ySpeed); pause(PAUSE_TIME); ySpeed += GRAVITY; } s.removeFromCanvas(); } }