java - 按键游戏后获得反应时间
问题描述
因此,我正在制作一款游戏,它会在您看到屏幕上弹出某些内容后记录您的反应时间,但我无法获得该反应时间。我希望用户在看到蓝色球后按向上箭头键,并且我想在他们按下该按钮后记录他们的反应时间。
这是我的代码:
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”,一旦用户按下向上箭头,我想在其中获取时间,但它不起作用。
如果有什么没有意义或不够清楚,我会尽力详细说明
解决方案
我想出了以下GUI。
我想解释两个重要的原则。首先是创建 GUI 是一个独立于更新 GUI 的过程。二是游戏过程是一个状态机。游戏处于六个不同的状态。这是我为牢记州而写的内容。
事件顺序
- 左键单击按钮
- 等待 2 - 4 秒以显示圆圈。
- 捕捉开始时间
- 左键单击按钮
- 捕捉结束时间。
- 计算并显示反应时间。
- 重复 1 - 6。
所以,对于 GUI,我创建了一个JFrame
和三个JPanels
;鞋面JPanel
、绘图JPanel
和按钮JPanel
。
我通过调用该SwingUtilities
invokeLater
方法启动了 Swing 应用程序。此方法确保在Event Dispatch Thread上创建和执行 Swing 组件。
JFrame
有一个默认的BorderLayout
,我用来放置三个JPanels
。JFrame
方法调用必须按特定顺序执行。这是我用于所有 Swing 应用程序的顺序。
上部JPanel
包含说明和反应时间显示。AJTextArea
非常适合显示说明。我使用 a 将内部放入JTextArea
内部,然后JPanel
使用 aFlowLayout
将其放置在鞋面JPanel
中BorderLayout
。像这样的嵌套布局是一种以逻辑方式组织 Swing 组件的好方法。
我将反应时间 Swing 组件放在另一个内部JPanel
,我放置在鞋面中JPanel
。
我创建了一幅画JPanel
,所以我不必为图像而烦恼。
该按钮JPanel
持有 Submit JButton
。
我创建了两个控制器类。一个控制器类 ,ButtonListener
响应JButton
左键单击。另一个控制器类,TimerListener
,创建绘制圆的延迟。
state 变量允许我用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();
}
}
}
}
推荐阅读
- reactjs - 我可以保证在表单提交之前状态已经改变吗?
- r - 如何将调色板添加到 geom_smooth 并删除图例中的灰色背景?
- jquery - 无法在 Jquery 数据表中获取隐藏列值
- html - 如何右对齐水平表格
- swiftui - 如何创建一个改变不透明度并在触摸时触发按钮动作的 SwiftUI 按钮?
- jmeter - 获取 java.util.concurrent.ExecutionException: java.lang.IllegalStateException: No Client ALPNProcessors!对于 jmeter
- html - 每个屏幕分辨率的某些 CSS 加载方式不同
- python - 连接 noneType 和字符串值列(熊猫数据框)导致“NaN”
- node.js - websocket 客户端:node.js 真的比 go(gorilla 和 gobwas)快吗?
- javascript - 永远订阅循环中的 For 循环