【问题标题】:Getting reaction time after pressing a key game按键游戏后获得反应时间
【发布时间】:2021-08-15 18:53:35
【问题描述】:

因此,我正在制作一款游戏,在您看到屏幕上弹出某些内容后记录您的反应时间,但我无法获得该反应时间。我希望用户在看到蓝色球后按下向上箭头键,并且我想记录他们按下该按钮后的反应时间。

这是我的代码:

public class Game extends JPanel 
{

private JLabel start, main, time;
private ImageIcon constant, react;

final int width = 600;
final int height = 600;

private Timer replace;


private Random random;
private int randTime;

private long startTime;
private long stopTime;
private long reactionTime;
private Action upAction;


public Game()
{

  setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

  setPreferredSize(new Dimension(width, height));
  setBackground(Color.black);

  start = new JLabel("Click Up Arrow when you see a blue ball");
  start.setForeground(Color.white);
  start.setAlignmentX(Component.CENTER_ALIGNMENT);
  add(start);
 
  constant = new ImageIcon("constantCircle.png");
  main = new JLabel(constant);
  main.setAlignmentX(Component.CENTER_ALIGNMENT);


  randomTime();
  replace = new Timer(randTime, timeListener);
  startTime = System.currentTimeMillis();
  replace.setRepeats(false);
  replace.start();    
  add(main);

  time = new JLabel("0");

  time.getInputMap().put(KeyStroke.getKeyStroke("UP"), "upAction");
  time.getActionMap().put("upAction", upAction);


  add(time);

}


public void randomTime()
{
  random = new Random();
  int max = 8000;
  randTime = random.nextInt(max);

}


ActionListener timeListener = new ActionListener()
{
  public void actionPerformed (ActionEvent e)
  {
    react = new ImageIcon("reactCircle.png");
    main.setIcon(react);     
  }
};

public class UpAction extends AbstractAction
{
  public void actionPerformed(ActionEvent e)
  {
    stopTime = System.currentTimeMillis();
    reactionTime = stopTime - startTime;
    time.setText("" + reactionTime);
  
  }
}  
}

我使用 System.currentTimeMillis 设置了一个“startTime”来获取球变蓝后的时间,但我不确定这是否是正确的方法。

我还在“UpAction”类中设置了一个“stopTime”,一旦用户按下向上箭头,我想在其中获取时间,但它不起作用。

如果有什么不明白或不够清楚的地方,我会尽力详细说明

【问题讨论】:

  • 1) 发布正确的minimal reproducible example 来证明问题。没有 main() 方法或 JFrame。我们无法复制/粘贴/编译和测试发布的代码。 2)基本代码看起来很合理。但是,您需要使用正确的InputMap。 JLabel 没有焦点,因此不会接收关键事件。正如您在上一个问题中所述:stackoverflow.com/questions/67672782/…A Key Binding can be configured to receive the KeyEvent even when the panel doesn't have focus.。因此,请重新阅读您获得的教程链接并使用适当的InputMap

标签: java swing jframe jpanel key-bindings


【解决方案1】:

我想出了以下 GUI。

我想解释两个重要的原则。首先是创建 GUI 是一个独立于更新 GUI 的过程。二是游戏过程是一个状态机。游戏处于六个不同的状态。这是我为牢记州而写的内容。

事件顺序

  1. 左键单击
  2. 等待 2 - 4 秒以显示圆圈。
  3. 捕获开始时间
  4. 左键单击
  5. 捕获结束时间。
  6. 计算并显示反应时间。
  7. 重复 1 - 6。

所以,对于 GUI,我创建了一个 JFrame 和三个 JPanels;一个上部JPanel,一个绘图JPanel,和一个按钮JPanel

我通过调用 SwingUtilities invokeLater 方法启动了 Swing 应用程序。此方法确保在Event Dispatch Thread 上创建和执行 Swing 组件。

JFrame 有一个默认的BorderLayout,我用来放置三个JPanelsJFrame 方法调用必须按特定顺序执行。这是我用于所有 Swing 应用程序的顺序。

上面的JPanel 包含指令和反应时间显示。 JTextArea 非常适合显示说明。我使用FlowLayoutJTextArea 放在内部JPanel 中,我使用BorderLayout 将其放在上部JPanel 中。像这样嵌套布局是一种以逻辑方式组织 Swing 组件的好方法。

我把反应时间 Swing 组件放在另一个内部的JPanel,我把它放在上面的JPanel

我创建了一个绘图JPanel,这样我就不必为图像而烦恼了。

按钮JPanel 持有提交JButton

我创建了两个控制器类。一个控制器类ButtonListener 响应JButton 左键单击。另一个控制器类 TimerListener 为绘制圆创建了延迟。

ButtonListener 状态变量允许我使用相同的ActionListener 提供不同的功能。如果您愿意,可以编写单独的 ActionListener 类,每个函数一个。

通过将我的代码分为视图类和控制器类,我可以将我的关注点分开并一次专注于应用程序的一个部分。

这是完整的可运行代码。我将所有类都设为内部类,这样我就可以将这段代码作为一个块发布。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class ReactionTimeGame implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new ReactionTimeGame());
    }
    
    private long reactionTime;
    
    private DrawingPanel drawingPanel;
    
    private JTextField reactionTimeField;
    
    public ReactionTimeGame() {
        this.reactionTime = 0L;
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Reaction Time Game");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        frame.add(createUpperPanel(), BorderLayout.BEFORE_FIRST_LINE);
        this.drawingPanel = new DrawingPanel();
        frame.add(drawingPanel, BorderLayout.CENTER);
        frame.add(createButtonPanel(), BorderLayout.AFTER_LAST_LINE);
        
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }
    
    private JPanel createUpperPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        
        JPanel innerPanel = new JPanel(new FlowLayout());
        
        String instructions = "This game will test your reaction time. To play "
                + "the game, left-click on the Submit button.  After a random time "
                + "from 2 - 4 seconds, a circle will appear.  Left-click the "
                + "Submit button again.  Your reaction time will be displayed "
                + "above where the circle was.\n\n"
                + "Left-click the Submit button to start each round of the game.";
        JTextArea textArea = new JTextArea(7, 40);
        textArea.setEditable(false);
        textArea.setText(instructions);
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        innerPanel.add(textArea);
        
        panel.add(innerPanel, BorderLayout.BEFORE_FIRST_LINE);
        
        innerPanel = new JPanel(new FlowLayout());
        
        JLabel label = new JLabel("Reaction Time:");
        innerPanel.add(label);
        
        reactionTimeField = new JTextField(5);
        reactionTimeField.setEditable(false);
        updateReactionTime();
        innerPanel.add(reactionTimeField);
        
        label = new JLabel("seconds");
        innerPanel.add(label);
        
        panel.add(innerPanel, BorderLayout.AFTER_LAST_LINE);
        
        return panel;
    }
    
    private JPanel createButtonPanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        
        JButton button = new JButton("Submit");
        button.addActionListener(new ButtonListener());
        panel.add(button);
        
        return panel;
    }
    
    public void setReactionTime(long reactionTime) {
        this.reactionTime = reactionTime;
    }

    public void drawCircle() {
        drawingPanel.setDrawCircle(true);
        drawingPanel.repaint();
    }
    
    public void eraseCircle() {
        drawingPanel.setDrawCircle(false);
        drawingPanel.repaint();
    }
    
    public void updateReactionTime() {
        double time = 0.001 * reactionTime;
        reactionTimeField.setText(String.format("%.3f", time));
    }
    
    public class DrawingPanel extends JPanel {

        private static final long serialVersionUID = 1L;
        
        private boolean drawCircle;
        
        public DrawingPanel() {
            this.drawCircle = false;
            this.setPreferredSize(new Dimension(300, 300));
        }

        public void setDrawCircle(boolean drawCircle) {
            this.drawCircle = drawCircle;
        }
        
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            
            if (drawCircle) {
                int centerX = getWidth() / 2;
                int centerY = getHeight() / 2;
                int radius = Math.min(getWidth(), getHeight()) * 9 / 20;
                int diameter = radius + radius;
                g.setColor(Color.MAGENTA);
                g.fillOval(centerX - radius, centerY - radius, diameter, diameter);
            }
        }
        
    }
    
    public class ButtonListener implements ActionListener {
        
        private int state;
        
        private long startTime;
        
        private final Random random;
        
        private Timer timer;
        
        public ButtonListener() {
            this.state = 1;
            this.random = new Random();
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            switch (state) {
            case 1:
                int delay = random.nextInt(2000) + 2000;
                timer = new Timer(delay, new TimerListener(this));
                timer.start();
                state = 2;
                break;
            case 2:
                setEndTime(System.currentTimeMillis());
                eraseCircle();
                state = 1;
                break;
            }
        }

        public int getState() {
            return state;
        }

        public void setStartTime(long startTime) {
            this.startTime = startTime;
        }

        public void setEndTime(long endTime) {
            long elapsedTime = endTime - startTime;
            setReactionTime(elapsedTime);
            updateReactionTime();
        }
        
    }
    
    public class TimerListener implements ActionListener {
        
        private final ButtonListener listener;

        public TimerListener(ButtonListener listener) {
            this.listener = listener;
        }

        @Override
        public void actionPerformed(ActionEvent event) {
            Timer timer = (Timer) event.getSource();
            timer.stop();
            
            if (listener.getState() == 2) {
                listener.setStartTime(System.currentTimeMillis());
                drawCircle();
            }
        }
        
    }

}

【讨论】:

  • Ops 要求是:我希望用户在看到蓝球时按向上箭头键。这如何解决 OP 响应 KeyEvent 的问题?
  • @camickr:OP 可以用按键代替按键。这个答案的重点是向 OP 展示如何将创建 GUI 与更新 GUI 以及创建此类游戏所涉及的状态分开。
  • OP 可以用按键替换按钮按下 - 不,OP 不能,这就是提出问题的原因。我仍在等待 OP 发布适当的 MRE,以便我们可以帮助进行键绑定。
猜你喜欢
  • 1970-01-01
  • 2015-02-20
  • 1970-01-01
  • 1970-01-01
  • 2013-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多