简体   繁体   中英

java.util.Timer && Swing mishaps

Currently, I am making a turn based game that has a JLabel with time remaining, several other labels for question/answer/team scores, and a main panel with the game board.

First issue with this code: The GameTimer class is called too often, IE it isn't being called every second, but about 5-10 times per second. (The print statement that outputs "Timer: " + timeElapsed is printing every number up to around 80 (Timer: 1, Timer: 2, etc.) within about 10 seconds. As a side note, the time for the question never exceeds 15 seconds. Shouldn't the while loop stop before it goes haywire? This issue happens only on occasion. I assume it's a util.Timer related bug, not in my code, but I'm not too familiar with the inner workings of the class.

Second issue: The for:each loop is called in an infinite loop, as in it prints "Reached Questions. Number of questions: " + questions.size() and continues to the for:each loop indefinitely. I didn't think windowOpened() could be called multiple times, and I also used windowActivated() with the same results.

Final issue: The time label is only updated after timeElapsed exceeds question.getTime, meaning that the label will only have a negative number in it. The while loop should detect that timeElapsed is greater, and stop, but it doesn't.

I'm bewildered by all of these, any help is greatly appreciated.

A little history of the project: Before implementing windowListener, the class just waited 30 seconds before executing the for:each loop, which did not work because the window would be a gray screen (unloaded) until the timer was finished, and then it would load normally, but GameTimer started immediately after the 30 second timer. I then tried using a Thread to update itself every second, but that had similar issues to the previous attempt.

There are other classes other than this. These 2 are the ones I believe the errors are inside of:

The game screen code:

import java.awt.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import java.util.*;
import java.awt.event.*;

@SuppressWarnings("deprecation")
public class GameScreen implements WindowListener{
    private int team1Score = 0;
    private int team2Score = 0;
    private ArrayList<Question> questions;

    private Question cQuestion;

    private boolean start = false;

    private boolean timesUp = false;
    private boolean isCorrect = false;
    private java.util.Timer timer = new java.util.Timer();
    private int timeElapsed = 0;

    private boolean isTeam1 = true;

    private String correctAnswer = "";
    private String inputAnswer = "";
    private int numIncorrect = 0;

    private TopPanel topPanel = new TopPanel();

    public GameScreen(ArrayList<Question> questions) {
        JFrame gameFrame = new JFrame("Cavazos Math Game");

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        int width = (int)screenSize.getWidth();
        int height = (int)screenSize.getHeight();

        gameFrame.setSize(width, height);
        gameFrame.setResizable(false);
        gameFrame.setLocationRelativeTo(null);
        gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        topPanel.setSize(width, 200);

        this.questions = questions;

        BorderLayout layout = new BorderLayout();

        gameFrame.setLayout(layout);

        System.out.println("Reached Image Loading");

        try {
            BufferedImage fieldImage = ImageIO.read(new File("field2.png"));
            GamePanel gamePanel = new GamePanel(fieldImage);
            gamePanel.setSize(width, height - 200);
            gameFrame.add(gamePanel, BorderLayout.CENTER);
        }
        catch(Exception e) {System.out.println("RUH ROH");}

        System.out.println("Reached Layout Loading");

        gameFrame.add(topPanel, BorderLayout.NORTH);
        gameFrame.setVisible(true);

        gameFrame.addKeyListener(topPanel);

        gameFrame.addWindowListener(this);
    }

    public void windowDeactivated(WindowEvent e) {}

    public void windowIconified(WindowEvent e) {}

    public void windowDeiconified(WindowEvent e) {}

    public void windowClosed(WindowEvent e) {}

    public void windowClosing(WindowEvent e) {}

    public void windowActivated(WindowEvent e) {}

    public void windowOpened(WindowEvent e) {
        System.out.println("Reached Questions. Number of question: " + questions.size());

        for(Question question : questions) {
            timeElapsed = 0;
            cQuestion = question;
            timesUp = false;
            topPanel.setQuestion(question.getQuestionString());
            setTimer(question.getTime());
            correctAnswer = question.getAnswer();
            System.out.println(question.getTime() + "");
            while((timeElapsed < question.getTime()) && !isCorrect && (numIncorrect <= 4)) {
                if(!topPanel.getInput().equals("nothing")) { //has an answer been submitted?
                    inputAnswer = topPanel.getInput();
                    if(!inputAnswer.equals(correctAnswer)) { //is it the wrong answer
                        numIncorrect++;
                        topPanel.setAnswer("");
                    }
                    else {
                        numIncorrect = 0;
                        isCorrect = true;
                        if(isTeam1) {
                            team1Score += question.getPoints();
                        }
                        else {
                            team2Score += question.getPoints();
                        }
                    }
                }
                else {

                }
                topPanel.setTimer(cQuestion.getTime() - timeElapsed);
            }
            isTeam1 = !isTeam1;
        }
    }

    class GameTimer extends TimerTask {
        public void run() {
            System.out.println("Timer: " + timeElapsed);
            timeElapsed++;
            topPanel.setTimer(cQuestion.getTime() - timeElapsed);
        }
    }

    public void setTimer(int seconds) {
        System.out.println("Timer has started");
        timer = new java.util.Timer();
        timer.schedule(new GameTimer(), 0, 1000);
    }
}

Top panel code:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class TopPanel extends JPanel implements KeyListener {
    JLabel questionLabel = new JLabel("Test Question");
    JTextField answerLabel = new JTextField("Answer label");
    JTextField timer = new JTextField("Timer Label");
    JTextField team1 = new JTextField("Team 1: 0");
    JTextField team2 = new JTextField("Team 2: 0");

    JPanel teamScores = new JPanel();

    //timer.setEditable(false);
    //team1.setEditable(false);
    //team2.setEditable(false);
    //answerLabel.setEditable(false);

    String inputString = "";
    boolean doneInputting = false;

    GridLayout scoresLayout = new GridLayout(2,1);
    GridLayout topLayout = new GridLayout(1,4);

    public TopPanel() {
        teamScores.setLayout(scoresLayout);
        teamScores.add(team1);
        teamScores.add(team2);
        this.setLayout(topLayout);
        this.setSize(getWidth(), 100);
        this.add(questionLabel);
        this.add(answerLabel);
        this.add(timer);
        this.add(teamScores);

        Font topFont = new Font("Sans Serif", Font.PLAIN, 32);

        this.setFont(topFont);
    }

    public void setQuestion(String question) {
        questionLabel.setText(question);
    }

    public void setAnswer(String answer) {
        answerLabel.setText(answer);
    }

    public void setTimer(int time) {
        timer.setText(time + "");
    }

    public void setTeam1Score(int score) {
        team1.setText(score + "");
    }

    public void addTeam1Score(int score) {
        team1.setText(score + "");
    }

    public void setTeam2Score(int score) {
        team2.setText(score + "");
    }

    public String getInput() {
        if(doneInputting) {
            return inputString;
        }
        return "nothing";
    }

    public String getCurrentInput() {
        return inputString;
    }

    public void keyPressed(KeyEvent e) {

    }

    public void keyReleased(KeyEvent e) {

    }

    public void keyTyped(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_9) {
            inputString += "9";
        }
        else if(e.getKeyCode() == KeyEvent.VK_8) {
            inputString += "8";
        }
        else if(e.getKeyCode() == KeyEvent.VK_7) {
            inputString += "7";
        }
        else if(e.getKeyCode() == KeyEvent.VK_6) {
            inputString += "6";
        }
        else if(e.getKeyCode() == KeyEvent.VK_5) {
            inputString += "5";
        }
        else if(e.getKeyCode() == KeyEvent.VK_4) {
            inputString += "4";
        }
        else if(e.getKeyCode() == KeyEvent.VK_3) {
            inputString += "3";
        }
        else if(e.getKeyCode() == KeyEvent.VK_2) {
            inputString += "2";
        }
        else if(e.getKeyCode() == KeyEvent.VK_1) {
            inputString += "1";
        }
        else if(e.getKeyCode() == KeyEvent.VK_0) {
            inputString += "0";
        }
        else if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
            inputString = inputString.substring(0, inputString.length()-1);
        }
        else if(e.getKeyCode() == KeyEvent.VK_T) {
            inputString += "TRUE";
        }
        else if(e.getKeyCode() == KeyEvent.VK_F) {
            inputString += "FALSE";
        }
        else if(e.getKeyCode() == KeyEvent.VK_P) {
            inputString += "π";
        }
        else if(e.getKeyCode() == KeyEvent.VK_S) {
            inputString += "√";
        }
        else if(e.getKeyCode() == KeyEvent.VK_ENTER) {
            doneInputting = true;
        }
        else if(e.getKeyCode() == KeyEvent.VK_SLASH) {
            inputString += "/";
        }
        setAnswer(inputString);
    }
}

You're starting a new Timer object with each iteration of your while loop, and so this is advancing your timeElapsed field with extra speed, each time a new Timer instance is created within the loop. The solution is to not do this, to only use one Timer, or to stop a Timer before creating a new one,.... but having said this, you've other significant problems in your code including:

  • This is a Swing application and you shouldn't be using java.util.Timers but rather javax.swing.Timers or "Swing Timers". The reason for this is that Swing Timers work well with the Swing event thread while Utility Timers do not, and this can lead to occurrence of intermittent and devilishly hard to debug errors.
  • You shouldn't be using a while loop as you're doing as this risks blocking the Swing event thread (or EDT for "Event Dispatch Thread"), rendering your GUI frozen. Instead use your Timer as well as a State Machine design to prevent need for the while loop.

If you need more detailed help, then please respond to comments above, and help us with your minimal example program , one we can run and modify ourselves. We don't want to see your entire program, nor do we want links, but rather the smallest program that shows us your problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM