Search code examples
javatimervariable-initialization

Is there a way in Java to make labels move without user influence?


I am trying to develop an application where the user moves a can back and forth on the bottom of the screen to try to catch bees as they fall. I have successfullly gotten the can movement working, but I am stuck on the bees' movement. In my current phase, I have a single bee imported that I want to fall from the top of the screen to the bottom, but it just stays at the top of the screen when it runs. I get an error here:

beeTimer.stop();

The error message: The local variable beeTimer may not have been initialized

Here is where it was initialized:

try {
            Timer beeTimer = new Timer(500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    System.out.println("bee movin!");
                    beeY[0] += beeSpeed;
                    beeLabel.setLocation(beeX, beeY[0]);

                    if (beeY[0] + 106 > (frame.getContentPane().getHeight() - 200)) {
                        beeTimer.stop();
                    }
                }           
            });
            beeTimer.start();
        } finally{}

and here is my entire code up to now:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import javax.swing.*;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;

//let the can be draggable by the mouse
class DraggableLabel extends JLabel implements MouseListener, MouseMotionListener {
    private Point mouseOffset;

    public DraggableLabel(ImageIcon icon) {
        super(icon);
        addMouseListener(this);
        addMouseMotionListener(this);
    }

    public void mousePressed(MouseEvent e) {
        mouseOffset = e.getPoint();
    }

    public void mouseDragged(MouseEvent e) {
        Point newMousePos = e.getLocationOnScreen();
        newMousePos.translate(-mouseOffset.x, -(newMousePos.y - getLocation().y));
        setLocation(newMousePos);
    }

    // The following methods are not needed for dragging, but must be implemented because we implement the MouseListener and MouseMotionListener interfaces.
    public void mouseClicked(MouseEvent e) {}
    public void mouseEntered(MouseEvent e) {}
    public void mouseExited(MouseEvent e) {}
    public void mouseReleased(MouseEvent e) {}
    public void mouseMoved(MouseEvent e) {}
}

class App{
    public static void main(String[] args){
        //declare the frame, or window of the game
        JFrame frame = new JFrame("Game");

        //declare the image variables
        ImageIcon can = null;
        ImageIcon bee = null;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

        //load the can image file
        try {
            File canFile = new File("assets/can.png");
            BufferedImage originalCan = ImageIO.read(canFile);
            if (originalCan == null) {
                throw new IOException("Unable to read image file: " + canFile.getName());
            }

            int canWidth = 75;
            int canHeight = 136; 
            Image icon = originalCan.getScaledInstance(canWidth, canHeight, Image.SCALE_SMOOTH);

            can = new ImageIcon(icon);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //load the bee image file
        try {
            File beeFile = new File("assets/bee.png");
            BufferedImage originalBee = ImageIO.read(beeFile);
            if (originalBee == null) {
                throw new IOException("Unable to read image file: " + beeFile.getName());
            }

            int beeWidth = 40;
            int beeHeight = 61; 
            Image icon = originalBee.getScaledInstance(beeWidth, beeHeight, Image.SCALE_SMOOTH);

            bee = new ImageIcon(icon);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //import the bee image
        Image beeImage = bee.getImage();
        ImageIcon beeFall = new ImageIcon(beeImage);
        JLabel beeLabel = new JLabel(beeFall);
        
        //set bee spawn location
        int beeX = (screenSize.width - bee.getIconWidth()) / 2;
        final int[] beeY = {0};
        beeLabel.setBounds(beeX, beeY[0], 60, 106);
        frame.getContentPane().add(beeLabel);

        //bee falling behavior
        int beeSpeed = 5;
        try {
            Timer beeTimer = new Timer(500, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    System.out.println("bee movin!");
                    beeY[0] += beeSpeed;
                    beeLabel.setLocation(beeX, beeY[0]);

                    if (beeY[0] + 106 > (frame.getContentPane().getHeight() - 200)) {
                        beeTimer.stop();
                    }
                }           
            });
            beeTimer.start();
        } finally{}

        //import the can image as draggable
        DraggableLabel label = new DraggableLabel(can);
        frame.getContentPane().setLayout(null);
        label.setSize(can.getIconWidth(), can.getIconHeight());
        frame.getContentPane().add(label);

        // Position can in the center horizontally and just above the bottom vertically
        int x = (screenSize.width - can.getIconWidth()) / 2;
        int y = screenSize.height - can.getIconHeight() - 175; // 50 pixels above the bottom
        label.setLocation(x, y);

        //make frame visible
        frame.setExtendedState(Frame.MAXIMIZED_BOTH);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);
        }
}

Any suggestions would be greatly appreciated!


Solution

  • For your game do not use swing labels or buttons at all. Instead, create one component (derived from e.g. JPanel) and override the paintComponent() method.

    Use Swing Timers to trigger 'automatic' actions, use Mouse and Keyboard Events to trigger user triggered actions. Calculate the state, then use repaint() to tell Swing it needs to render the component. Swing will do it as soon as it 'feels like'.

    For more hints/libraries just use the correct StackOverflow search: https://stackoverflow.com/search?q=java+game+programming