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();
}
}