How To Create Image Puzzle Game in Java NetBeans
In this Java Tutorial we will see How to Create a picture puzzle game using Java Swing in Netbeans.
This java swing puzzle game is easy to use, player can upload his own images and Select the difficulty level from multiple levels (4x4 to 8x8 grid).
The Game Features:
- Upload your own images.
- Multiple difficulty levels (4x4 to 8x8 grid).
- Move counter and timer.
- Smooth piece-swapping animations.
- Winning detection and celebration.
How the Game Works:
When you start the game, you'll see a wimdow split into two parts:
- A sidebar with controls and game stats.
- The main puzzle grid area.
You can upload any image, and the game will automatically:
- Resize it to fit the game window.
- Split it into equal pieces based on your chosen difficulty.
- Shuffle the pieces for you to solve.
What We Are Gonna Use In This Project:
- Java Programming Language.- NetBeans Editor.
Project Source Code:
/**
* Initializes the game timer that updates every second
*/
private void initializeTimer() {
timer = new Timer(1000, e -> {
elapsedTime++; // Increment elapsed time
int minutes = elapsedTime / 60; // Calculate minutes
int seconds = elapsedTime % 60; // Calculate seconds
timerLabel.setText(String.format("Time: %02d:%02d", minutes, seconds)); // Update timer display
});
}
/**
* Creates a panel to display game statistics (moves and time)
*/
private JPanel createStatsPanel() {
// Create panel with vertical box layout
JPanel statsPanel = new JPanel();
statsPanel.setLayout(new BoxLayout(statsPanel, BoxLayout.Y_AXIS));
statsPanel.setOpaque(false);
// Add semi-transparent border
statsPanel.setBorder(BorderFactory.createCompoundBorder(
new LineBorder(new Color(255, 255, 255, 50), 1, true),
new EmptyBorder(15, 15, 15, 15)));
// Add statistics elements
JLabel statsTitle = createLabel("Game Stats", 20, Font.BOLD);
statsPanel.add(statsTitle);
statsPanel.add(Box.createVerticalStrut(10));
statsPanel.add(movesLabel);
statsPanel.add(Box.createVerticalStrut(5));
statsPanel.add(timerLabel);
return statsPanel;
}
/**
* Creates a styled label with specified text, font size, and style
*/
private JLabel createLabel(String text, int fontSize, int fontStyle) {
JLabel label = new JLabel(text);
label.setFont(new Font("Arial", fontStyle, fontSize)); // Set font properties
label.setForeground(Color.WHITE); // Set text color
label.setAlignmentX(Component.CENTER_ALIGNMENT); // Center align
return label;
}
/**
* Creates a styled button with specified text and background color
*/
private JButton createButton(String text, Color color) {
// Create button with custom painting for rounded corners
JButton button = new JButton(text) {
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(getBackground());
g2d.fill(new RoundRectangle2D.Float(0,0, getWidth(), getHeight(), 15, 15));
g2d.dispose();
super.paintComponent(g);
}
};
// Style the button
button.setAlignmentX(Component.CENTER_ALIGNMENT);
button.setMaximumSize(new Dimension(240, 50));
button.setFont(new Font("Arial", Font.BOLD, 16));
button.setForeground(Color.WHITE);
button.setBackground(color);
button.setBorder(new EmptyBorder(10, 15, 10, 15));
button.setFocusPainted(false);
button.setContentAreaFilled(false);
// Add hover effect
button.addMouseListener(new MouseAdapter() {
@Override
public void mouseEntered(MouseEvent evt) {
button.setBackground(color.darker()); // Darken on hover
}
@Override
public void mouseExited(MouseEvent evt) {
button.setBackground(color); // Restore original color
}
});
return button;
}
/**
* Applies custom styling to the difficulty combo box
*/
private void styleCombobox(JComboBox<String> combobox) {
combobox.setMaximumSize(new Dimension(240, 40));
combobox.setFont(new Font("Arial", Font.PLAIN, 16));
combobox.setBackground(new Color(236, 240, 241));
combobox.setForeground(new Color(44, 62, 80));
combobox.setBorder(BorderFactory.createCompoundBorder(
new LineBorder(new Color(189, 195, 199), 1),
new EmptyBorder(5, 10, 5, 10)
));
combobox.setAlignmentX(Component.CENTER_ALIGNMENT);
// Center align items in dropdown
combobox.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel)super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus);
label.setHorizontalAlignment(JLabel.CENTER);
return label;
}
});
}
/**
* Opens file chooser and loads selected image to start puzzle
*/
private void loadAndStartPuzzle() {
JFileChooser fileChooser = new JFileChooser();
// Set filter to only show image files
fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter(
"Image Files", ImageIO.getReaderFileSuffixes()));
int returnValue = fileChooser.showOpenDialog(null);
if(returnValue == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
try {
originalImage = ImageIO.read(selectedFile); // Load image
BufferedImage resizedImage = resizeImage(originalImage, IMAGE_SIZE, IMAGE_SIZE); // Resize
resetGame(resizedImage); // Start new game
} catch (IOException ex) {
Logger.getLogger(Java_Puzzle_Game.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
/**
* Resizes an image to specified dimensions while maintaining aspect ratio
*/
private BufferedImage resizeImage(BufferedImage image, int targetWidth, int targetHeight) {
// Create scaled instance of image
Image reszedImage = image.getScaledInstance(targetWidth, targetHeight, Image.SCALE_SMOOTH);
BufferedImage bufferedReszedImage = new BufferedImage(targetWidth, targetHeight,
BufferedImage.TYPE_INT_ARGB);
// Draw scaled image
Graphics2D g2d = (Graphics2D) bufferedReszedImage.createGraphics();
g2d.drawImage(reszedImage, 0, 0, null);
g2d.dispose();
return bufferedReszedImage;
}
/**
* Resets the game state and creates new puzzle pieces from the provided image
*/
private void resetGame(BufferedImage image) {
// Reset game statistics
timer.stop();
elapsedTime = 0;
timerLabel.setText("Timer: 00:00");
moves = 0;
movesLabel.setText("Moves: 0");
// Clear existing puzzle pieces
gridPanel.removeAll();
labelList.clear();
correctOrder.clear();
firstSelectedSlice = null;
secondSelectedSlice = null;
// Calculate dimensions for puzzle pieces
int sliceWidth = image.getWidth() / GRID_SIZE;
int sliceHeight = image.getHeight() / GRID_SIZE;
List<ImageIcon> imageSlices = new ArrayList<>();
// Create puzzle pieces
for(int y = 0; y < GRID_SIZE; y++) {
// Create puzzle pieces from subimages
for(int x = 0; x < GRID_SIZE; x++) {
// Extract piece from original image
BufferedImage slice = image.getSubimage(
x * sliceWidth,
y * sliceHeight,
sliceWidth,
sliceHeight
);
// Create icon from slice and add to lists
ImageIcon imageIcon = new ImageIcon(
slice.getScaledInstance(sliceWidth, sliceHeight, Image.SCALE_SMOOTH)
);
imageSlices.add(imageIcon);
correctOrder.add(imageIcon);
}
}
// Randomly shuffle the puzzle pieces
Collections.shuffle(imageSlices);
// Create and add labels for each puzzle piece
for(ImageIcon icon : imageSlices) {
// Create label for puzzle piece
JLabel sliceLabel = new JLabel(icon);
sliceLabel.setBorder(BorderFactory.createLineBorder(new Color(189, 195, 199), 2));
sliceLabel.setHorizontalAlignment(JLabel.CENTER);
sliceLabel.setVerticalAlignment(JLabel.CENTER);
// Add click listener to handle piece selection
sliceLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
handleSliceClick(sliceLabel);
}
});
// Add to tracking lists and grid
labelList.add(sliceLabel);
gridPanel.add(sliceLabel);
}
// Refresh the display
gridPanel.revalidate();
gridPanel.repaint();
// Start the game timer
timer.start();
}
/**
* Updates the grid size based on selected difficulty level
*/
private void setGridSize(String difficulty) {
// Set grid dimensions based on selection
switch (difficulty) {
case "4x4" -> GRID_SIZE = 4;
case "5x5" -> GRID_SIZE = 5;
case "6x6" -> GRID_SIZE = 6;
case "7x7" -> GRID_SIZE = 7;
case "8x8" -> GRID_SIZE = 9;
}
// Update grid layout
gridPanel.setLayout(new GridLayout(GRID_SIZE, GRID_SIZE));
}
/**
* Handles click events on puzzle pieces
*/
private void handleSliceClick(JLabel clickedLabel) {
if(firstSelectedSlice == null) {
// First piece selection
firstSelectedSlice = clickedLabel;
firstSelectedSlice.setBorder(BorderFactory.createLineBorder(new Color(231,76,60), 2));
}
else if(secondSelectedSlice == null && clickedLabel != firstSelectedSlice) {
// Second piece selection
secondSelectedSlice = clickedLabel;
secondSelectedSlice.setBorder(BorderFactory.createLineBorder(new Color(231,76,60), 2));
// Start swap animation
swapSlices();
}
}
/**
* Animates the swapping of two puzzle pieces
*/
private void swapSlices() {
Timer animationTimer = new Timer(10, null);
final int animationDuration = 500; // Animation duration in milliseconds
final long startTime = System.currentTimeMillis();
// Store initial positions
Point firstLocation = firstSelectedSlice.getLocation();
Point secondLocation = secondSelectedSlice.getLocation();
// Animation update handler
animationTimer.addActionListener((e) -> {
// Calculate animation progress
long elapsed = System.currentTimeMillis() - startTime;
float progress = Math.min(1f, (float) elapsed / animationDuration);
float easedProgress = easeInOut(progress); // Apply easing for smooth movement
// Calculate new positions
int x1 = (int) (firstLocation.x + (secondLocation.x - firstLocation.x) * easedProgress);
int y1 = (int) (firstLocation.y + (secondLocation.y - firstLocation.y) * easedProgress);
int x2 = (int) (secondLocation.x + (firstLocation.x - secondLocation.x) * easedProgress);
int y2 = (int) (secondLocation.y + (firstLocation.y - secondLocation.y) * easedProgress);
// Update piece positions
firstSelectedSlice.setLocation(x1, y1);
secondSelectedSlice.setLocation(x2, y2);
// Check if animation is complete
if(progress >= 1f) {
animationTimer.stop();
finalizeSwap();
}
});
// Start animation
animationTimer.start();
}
/**
* Easing function for smooth animation
* Creates acceleration at start and deceleration at end
*/
private float easeInOut(float t) {
return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
}
/**
* Completes the swap operation after animation
*/
private void finalizeSwap() {
// Swap the icons between pieces
Icon tempIcon = firstSelectedSlice.getIcon();
firstSelectedSlice.setIcon(secondSelectedSlice.getIcon());
secondSelectedSlice.setIcon(tempIcon);
// Reset pieces to their grid positions
firstSelectedSlice.setLocation(firstSelectedSlice.getParent().getComponent(
labelList.indexOf(firstSelectedSlice)).getLocation()
);
secondSelectedSlice.setLocation(secondSelectedSlice.getParent().getComponent(
labelList.indexOf(secondSelectedSlice)).getLocation()
);
// Reset borders to default
firstSelectedSlice.setBorder(BorderFactory.createLineBorder(new Color(189, 195, 199), 2));
secondSelectedSlice.setBorder(BorderFactory.createLineBorder(new Color(189, 195, 199), 2));
// Clear selection
firstSelectedSlice = null;
secondSelectedSlice = null;
// Update move counter
moves++;
movesLabel.setText("Moves: " + moves);
// Check if puzzle is solved
checkIfSolved();
}
/**
* Checks if the puzzle has been solved
*/
private void checkIfSolved() {
// Compare current arrangement with correct order
for(int i = 0; i < labelList.size(); i++) {
if(labelList.get(i).getIcon() != correctOrder.get(i)) {
return; // Puzzle not solved
}
}
// Puzzle is solved
timer.stop();
showCongratulationDialog();
}
/**
* Shows congratulation dialog when puzzle is solved
*/
private void showCongratulationDialog() {
// Create modal dialog
JDialog dialog = new JDialog(this, "Congratulation!", true);
dialog.setLayout(new BorderLayout());
dialog.setSize(300, 200);
dialog.setLocationRelativeTo(this);
// Create panel with gradient background
JPanel panel = new JPanel() {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
// Create gradient background
int w = getWidth();
int h = getHeight();
Color color1 = new Color(41, 128, 185);
Color color2 = new Color(44, 62, 80);
GradientPaint gp = new GradientPaint(0, 0, color1, w, h, color2);
g2d.setPaint(gp);
g2d.fillRect(0, 0, w, h);
}
};
// Configure panel layout
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(new EmptyBorder(20, 20, 20, 20));
// Add congratulation message and stats
JLabel congratsLabel = createLabel("Puzzle Solved!", 24, Font.BOLD);
JLabel statsLabel = createLabel("Moves: " + moves + " | Time: " + formatTime(elapsedTime), 16, Font.PLAIN);
// Add close button
JButton closeButton = createButton("Close", new Color(231, 76, 60));
closeButton.addActionListener((ActionEvent e) -> dialog.dispose());
// Add components to panel
panel.add(congratsLabel);
panel.add(Box.createVerticalStrut(10));
panel.add(statsLabel);
panel.add(Box.createVerticalStrut(20));
panel.add(closeButton);
// Show dialog
dialog.add(panel);
dialog.setVisible(true);
}
/**
* Formats time in minutes:seconds format
*/
private String formatTime(int seconds) {
int minutes = seconds / 60;
int remainingSeconds = seconds % 60;
return String.format("%02d:%02d", minutes, remainingSeconds);
}
/**
* Shuffles the puzzle pieces on the grid
*/
private void shuffleGrid() {
// Reset game statistics
timer.stop();
elapsedTime = 0;
timerLabel.setText("Timer: 00:00");
moves = 0;
movesLabel.setText("Moves: 0");
// Create and shuffle new arrangement
List<ImageIcon> shuffledIcons = new ArrayList<>(correctOrder);
Collections.shuffle(shuffledIcons);
// Apply new arrangement to grid
for(int i = 0; i < labelList.size(); i++) {
labelList.get(i).setIcon(shuffledIcons.get(i));
}
// Start timer if game is active
if(originalImage != null) {
timer.start();
}
}
The Final Result:
if you want the source code click on the download button below
disclaimer: you will get the source code with the database script and to make it work in your machine is your responsibility and to debug any error/exception is your responsibility this project is for the students who want to see an example and read the code not to get and run.
More Java Projects:
Download Projects Source Code