1
votes

So i'm making a game using Java and want to use a timer to countdown from 3 on the game screen after the user presses "play" on the menu screen.

Here's my code:

int seconds = 3;

private void countdown(Graphics g) {

    Timer timer = new Timer();

    TimerTask task = new TimerTask() {
        public void run() {
            if(seconds >= 0) {
                System.out.println(seconds);
                seconds--;
            }
            if(seconds == -1) {

                timer.cancel();
            }
        }
    };

    timer.schedule(task, 1000, 1000);
}

I use countdown in the draw method of my game here:

public void draw(Graphics g) {

    //background
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, WIDTH, HEIGHT);

    //count
    countdown(g);
}

I'm using the console to print the numbers however I will change this to use drawString() later to draw the countdown on screen.

After pressing "play" on my menu screen there is a one second pause on my game screen and then the numbers all print to the console. The problem is that there is no one second delay between the numbers printing, they just print all at the same time. Any help?

3
Works fine for me. Please post a minimal reproducible example. - shmosel
Did you use a draw method and did the seconds decrement with 1 second intervals? - Jared Butterfield
I just did the countdown() method and it counted down to 0 in 1-second intervals. I don't see how the draw() method is relevant to the question. - shmosel
the draw function is calling the countdown function which makes it relevant in this case - lostbard
Well what I initially intended to do was use graphics to draw a string on the screen that displayed the countdown, and not use the console. The console was just for testing purposes. - Jared Butterfield

3 Answers

1
votes

If draw gets called repeatedly, then multiple timers will be created, so you will get print outs from each timer, which will also be decrementing the seconds count.

You could try creating a single timer rather than creating in each countdown function and just have the countdown function set the seconds value.

1
votes

Your program appears to be a Swing or AWT GUI program, and if so, you're using the wrong type of Timer, here a java.util.Timer and java.util.TimerTask, both of which are not Swing thread safe. This can cause your GUI to freeze, and the numbers not show up, just as you're seeing. Instead use a timer built specifically for these GUI's, a Swing Timer (click on this link for the tutorial). You're also calling the timer from the drawing code which is the exact opposite of what you should be doing. Instead the Timer should change the state of your counting variable and then call repaint(). The drawing code then gets the value of the counting variable and displays it within the GUI.

For example, your Swing Timer could use an ActionListener whose actionPerformed method looks something like this:

public void actionPerformed(ActionEvent e) {
    if (counter != 0) {
        // if the counter hasn't reached the end
        counter--;  // reduce counter by one
        repaint();  // tell the GUI to repaint
    } else {
        timer.stop();  // otherwise at the end, so stop the timer
        countDownAction.setEnabled(true); // and re-enable the button
    }
}

A complete GUI example could look like this:

import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;

@SuppressWarnings("serial")
public class CountDown extends JPanel {
    public static final int TIMER_DLEAY = 1000;
    private static final Font COUNTER_FONT = new Font(Font.SANS_SERIF, Font.BOLD, 400);
    private static final int TEXT_X = 200;
    private static final int TEXT_Y = 500;
    private int counter;
    private JSpinner spinner = new JSpinner(new SpinnerNumberModel(5, 0, 20, 1));
    private CountDownAction countDownAction = new CountDownAction("Count Down");
    private JButton countDownButton = new JButton(countDownAction);
    private Timer timer;

    public CountDown() {
        setPreferredSize(new Dimension(800, 600));
        add(new JLabel("Starting Value:"));
        add(spinner);
        add(countDownButton);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, 
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g.setFont(COUNTER_FONT);
        String text = String.format("%02d", counter);
        g.drawString(text, TEXT_X, TEXT_Y);
    }

    private class CountDownAction extends AbstractAction {
        public CountDownAction(String name) {
            super(name);
            int mnemonic = (int) name.charAt(0);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            // inactivate the button/action
            countDownAction.setEnabled(false);
            counter = (int) spinner.getValue();
            repaint();
            timer = new Timer(TIMER_DLEAY, new TimerListener());
            timer.start();
        }
    }

    private class TimerListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            if (counter != 0) {
                // if the counter hasn't reached the end
                counter--;  // reduce counter by one
                repaint();  // tell the GUI to repaint
            } else {
                timer.stop();  // otherwise at the end, so stop the timer
                countDownAction.setEnabled(true); // and re-enable the button
            }
        }
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("CountDown");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new CountDown());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}
0
votes

You must reset your seconds variable when you are creating and/or cancelling the timer

        Timer timer = new Timer();
        seconds = 3; //Reset here
        ..........
        ..........

        if(seconds == -1) {
            timer.cancel();
            seconds = 3; // Reset here
        }