Bubble Burst
Bubble Burst
Develop a Bubble Burst game where the objective is for a player to burst all the bubbles on
the playing field before the timer expires. (100 points)
Requirements:
I. The system shall have two GUIs (JFrames). (20 points)
A. The first GUI shall have two Jbuttons (Start and Restart) and a JSlider. The Start
button shall be used to start the game. Once it’s pressed the second GUI displays
and the game starts. Once the game ends, the Restart button shall be used to
start the game over. The JSlider shall be used for the player to select the game
difficulty: Easy (4 bubbles), Medium (5 bubbles), Hard (6 bubbles).
B. The second GUI should contain the field where the game will be played.
Solution: Designed the user interface with two JFrame windows. The mainFrame
showcases the JSlider and start/restart buttons, while the playAreaFrame
presents the game screen.
II. The Playing Field
A. The playing field shall be defined as the dimensions of the JPanel.
Solution: The second screenshot above showcases the game's playing field, established by a
JPanel component named playAreaPanel that resides within the playAreaFrame JFrame.
Starting from round two onwards, the repositionGlobal method is invoked at the
beginning of each round to generate random bubble positions. This method utilizes Java's
Random class to determine appropriate x and y coordinates for each bubble. To ensure
proper placement, bubbles are only drawn if they fall within the panel boundaries and do not
overlap with existing bubbles.
IV. Reposition_Local
A. Repositions bubbles by choosing a random coordinate from each bubble’s local
neighborhood. The local neighborhood is defined as a subset of the playing field where
the bubble is allowed to roam. For example, if a bubble’s center coordinate is (100, 100)
and we define a local neighborhood of 50, then the bubble can take a random hop in
the bounding box (neighborhood) – (50,150), (50, 150), (150, 50), (150,150).
Neighborhoods are drawn as rectangles for each bubble. Bubbles make hops in their
local neighborhoods until they are burst. Bubbles are not allowed to hop outside of the
playing field. The local neighborhood expands as the rounds increase. Assume a
neighborhood of 50 for Round 1, and increase by 18 for each additional round.
Solution: The proceedToNextRound method initiates a new round by repositioning all
bubbles randomly on the gamePanel using repositionGlobal method. This triggers the
repositionLocal method, which launches a separate thread. The RepositionBubbles
thread's run method executes the BubbleUtil.findValidRandomNeighbor function
repeatedly until all bubbles are eliminated. This function repositions each bubble within its
designated neighborhood (defined by neighborhoodSize) every 3 seconds, keeping the
game dynamic.
Moreover, handleClick method detects when player clicks elsewhere and not on the
bubble.
IX. Timer(10 points)
A. The timer starts at the beginning of each round. As the rounds increase, the time to
complete each round decreases. The round 1 timer is 15 seconds, and it decreases by 1
second each additional round. The timer shall be displayed.
Solution: The startTimer method establishes a countdown timer for each round. Initially
set to 15 seconds for Round 1, it decrements by 1 second for subsequent rounds. The timer is
then displayed prominently at the top of the panel.
Code
Main.java
public class Main{
public static void main(String[] args){
new GameField();
}
}
GameField.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.IntStream;
public GameField() {
mainFrame.add(panel1, BorderLayout.NORTH);
mainFrame.add(panel2, BorderLayout.CENTER);
mainFrame.setSize(400, 400);
mainFrame.setVisible(true);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
selectedBubbleCount = difficultySlider.getValue();
playAreaFrame = new JFrame("Bubble Burst Game");
playAreaFrame.setSize(600, 600);
playAreaFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
playAreaPanel = createPanel();
playAreaFrame.add(playAreaPanel);
playAreaFrame.setVisible(true);
showBubblePlacementGuide();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawOrigins(g);
}
};
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent event) {
if (currentRound == 0) {
setBubbleOrigins(event);
} else {
handleClick(event);
}
}
});
return panel;
}
}
}
@Override
public void actionPerformed(ActionEvent e) {
timeLeft--;
playAreaFrame.setTitle("Round-" + currentRound + ", Time
Left:" + timeLeft + " seconds");
if(timeLeft <= 0 && gameTimer.isRunning()) {
handleGameOver(JOptionPane.ERROR_MESSAGE, "Game Over
because Time is up, Try Again!");
}
}
};
} while (!BubbleUtil.isValidBubblePlacement(bubble,
playAreaPanel, bubbleOrigins, null));
return bubble;
}
if (selectedIndex != -1) {
bubbleOrigins.remove(selectedIndex);
fixedBubbleOriginsAtRoundStart.remove(selectedIndex);
playAreaPanel.repaint();
if (bubbleOrigins.isEmpty()) {
if (animationThread != null) {
animationThread.interrupt();
}
if (currentRound < 10) {
proceedToNextRound();
} else {
gameIsWon = true;
handleGameOver(JOptionPane.INFORMATION_MESSAGE, "You Won,
Congrats!");
}
}
}
else {
gameIsWon = false;
handleGameOver(JOptionPane.ERROR_MESSAGE, "You Lost as you
didn't click on bubble, try again");
}
}
gameIsWon = false;
currentRound = 0;
currentBubbleCount = 0;
bubbleOrigins = new CopyOnWriteArrayList<>();
fixedBubbleOriginsAtRoundStart = new ArrayList<>();
}
RepositionBubbles.java
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@Override
public void run() {
int neighborhoodSize = bubbleSize + 18 * (CURR_ROUND - 1);
List<Point> tempOrigins = new ArrayList<>();
for(Point origin: BUBBLE_ORIGINS) {
tempOrigins.add(new Point(origin));
}
while (!Thread.currentThread().isInterrupted() && !
BUBBLE_ORIGINS.isEmpty()) {
try {
Thread.sleep(3000);
GAME_PANEL.repaint();
} catch (InterruptedException e) {
}
}
}
}
BubbleUtil.java
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.IntStream;
class BubbleUtil {