【问题标题】:Event driven input vs turn based事件驱动输入与基于回合
【发布时间】:2016-08-27 13:42:43
【问题描述】:

我正在尝试使用 JTextArea 作为控制台/输出和 JTextField 作为用户输入来重新创建控制台游戏。由于 GUI 是事件驱动的,我不明白如何阻止代码执行,以等待用户输入,然后再继续对手转向。我能想到的唯一解决方案是 While(userTurn) 和 userTurn 将在 actionlistener 时更改为 false 是否有更好的方法?

我的控制台解决方案

 String getInput(String prompt){
        String inputLine = null;
      console.setTextConsole(prompt + " ");
        try{
            BufferedReader is = new BufferedReader(new InputStreamReader(System.in));
            inputLine = is.readLine();
            if(inputLine.length() == 0) return null;

        }catch(IOException e){
            console.setTextConsole("IOException "+e);
        }
        return inputLine;
    }

我刚刚调用了这个 getInput 方法,然后继续进行对手回合。

我想要完成的是:

  1. 对手转向
  2. 游戏等待用户
  3. 用户在 JtextField 中输入文本并按下回车键
  4. 游戏执行玩家命令
  5. 对手再次转向..

【问题讨论】:

  • 为什么你问了10个问题,却只接受了一个答案?
  • 是两人对战还是单对电脑?
  • 事件驱动代码的意思就是,作为事件结果执行的代码。当用户按下 Enter 键时,您执行 just 执行播放器命令的代码。您可以为对手的回合设置另一个按钮,该按钮将执行对手的回合代码。

标签: java swing jtextfield


【解决方案1】:

我编写了一个示例游戏,以便您观察其中的差异。计算机和用户尝试猜测 0-2(含)之间的随机数。谁做对了,谁就赢了。如果两者都做对了,或者都做错了,那就是平局。

编辑:更新的 GUI 版本

这是控制台程序:

import java.util.Random;
import java.util.Scanner;

public class ConsoleGame {
    public static void main(String[] args) {
        Scanner console = new Scanner(System.in);
        Random rand = new Random();
        boolean playAgain = false;
        int wins = 0, losses = 0, draw = 0;
        do {
            int num = rand.nextInt(3); // 0-2 inclusive
            System.out.println("Guess the number [0-2]: ");
            int guess = Integer.parseInt(console.nextLine());
            int computerGuess = rand.nextInt(3);
            System.out.println("You: " + guess + "\tComputer: " + computerGuess + "\tNumber: " + num);
            if (guess == num && computerGuess == num || guess != num && computerGuess != num) {
                draw++;
                System.out.println("Draw!");
            } else if (guess == num) {
                wins++;
                System.out.println("You win!");
            } else if (computerGuess == num) {
                losses++;
                System.out.println("Computer wins :(");
            }
            System.out.println("Play again [y/n]? ");
            playAgain = console.nextLine().startsWith("y");
        } while (playAgain);
        System.out.println("Wins: " + wins + "\nLosses: " + losses + "\nDraw: " + draw);
        console.close();
    }
}

GUI 程序如下:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class GUIGame extends JFrame {
    private JPanel contentPane;
    private JTextField textField;
    private JTextArea textArea;
    private boolean textReceived;

     /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    GUIGame frame = new GUIGame();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame.
     */
    public GUIGame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setLayout(new BorderLayout());
        setContentPane(contentPane);

        textField = new JTextField();
        textField.addActionListener(new ActionListener() {
            @Override
            // user pressed 'enter' key,
            public void actionPerformed(ActionEvent e) {
                textReceived = true;
                synchronized (textField) {
                    // notify game loop thread which is waiting on this event
                    textField.notifyAll();
                }
            }
        });
        contentPane.add(textField, BorderLayout.SOUTH);

        JScrollPane scrollPane = new JScrollPane();
        contentPane.add(scrollPane, BorderLayout.CENTER);

        textArea = new JTextArea();
        textArea.setFont(new Font("Consolas", Font.PLAIN, 12));
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        textArea.setForeground(Color.LIGHT_GRAY);
        textArea.setBackground(Color.BLACK);
        textArea.setEditable(false);
        scrollPane.setViewportView(textArea);

        // Start game loop in new thread since we block the thread when
        // waiting for input and we don't want to block the UI thread
        new Thread(new Runnable() {
            @Override
            public void run() {
                playGame();
            }
        }).start();
    }

    private void playGame() {
        Random rand = new Random();
        boolean playAgain = false;
        int wins = 0, losses = 0, draw = 0;
        do {
            int num = rand.nextInt(3); // 0-2 inclusive
            textArea.append("Guess the number [0-2]: \n");
            int guess = Integer.parseInt(requestInput());
            int computerGuess = rand.nextInt(3);
            textArea.append("You: " + guess + "\tComputer: " + computerGuess + "\tNumber: " + num + "\n");
            if (guess == num && computerGuess == num || guess != num && computerGuess != num) {
                draw++;
                textArea.append("Draw!\n");
            } else if (guess == num) {
                wins++;
                textArea.append("You win!\n");
            } else if (computerGuess == num) {
                losses++;
                textArea.append("Computer wins :(\n");
            }
            textArea.append("Play again [y/n]? \n");
            playAgain = requestInput().startsWith("y");
        } while (playAgain);
        textArea.append("Wins: " + wins + "\nLosses: " + losses + "\nDraw: " + draw + "\n");
    }

    private String requestInput() {
        textField.setEnabled(true);
        textField.requestFocus();
        // wait on text field till UI thread signals a user input event
        synchronized (textField) {
            while (!textReceived) {
                try {
                    textField.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        String input = textField.getText();
        textField.setText("");
        textField.setEnabled(false);
        textReceived = false;
        return input;
    }
}

【讨论】:

  • 干杯我想我开始明白了。如果您可以向我解释,另一件事是如果我期望不同的输入,例如在游戏开始时,我希望用户输入他的名字,然后希望他按 F,然后按 A / G 等。
  • 您通过提示来做到这一点,但这似乎不是最普遍的方式,还有其他方式吗?而不是回读 textarea?
  • @Higeath 是的,事实上还有一种更好、更直观的方法。尽管它类似于您在问题中提到的建议解决方案,但称为忙等待的技术会占用 CPU 资源。通过Object.wait() 阻止是更好的选择,请参阅更新后的答案。
【解决方案2】:

嗯,我认为是这样的:

游戏的统治者是谁拥有回合。只要统治者采取行动,对方就必须等待。如何实现?

  1. 如果用户拥有回合,他/她可以在 JTextField 中输入文本。

  2. 当他/她按 ENTER 时,必须验证命令。如果没问题,则必须将轮到程序转移到程序中,同时,用户甚至不能在 JTextField 中输入文本。例如,禁用它:

私人无效开关TurnToTheProgram() { jTextField.setEnabled(false); }
  1. 当程序完成移动时,必须再次将轮次转移给用户,因此必须启用 jTextField:
私人无效开关TurnToTheUser() { jTextField.setEnabled(true); }
  1. 最后,您必须在每种情况下确定谁是第一轮(以使 jTextField 显示为启用或禁用)。

完整的算法:

public void startGame(boolean userOwnsTheFirstTurn)
{
    if (userOwnsTheFirstTurn)
    {
        switchTurnToTheUser();
    }
    else
    {
         switchTurnToTheProgram();
         calculateNextMove();
         switchTurnToTheUser();
    }
}

public void userHasEnteredSomeCommand(String command)
{
    // This must be called from the correspondant actionListener.
    if (validateCommand())
    {
         switchTurnToTheProgram();
         calculateNextMove();
         switchTurnToTheUser();
    }
    else
    {
       ... log an error to the user ...
    }
}

为了增强用户体验,启用/禁用按钮以及文本字段可能会很有用。在这种情况下,您只需要修改switchTurnToTheProgramswitchTurnToTheUser 这两个方法。

【讨论】:

  • 我正在寻找的部分是等待用户按回车而不进一步使用代码
  • 这在 Java swing 中很直接:您只需打开并绘制 JPanel,添加侦听器,然后结束。面板将保持打开状态,并且有一个后台线程侦听用户事件,该线程将调用附加到按钮的 actionListener。
猜你喜欢
  • 2013-07-26
  • 1970-01-01
  • 1970-01-01
  • 2015-10-09
  • 2017-09-03
  • 2010-12-25
  • 2014-12-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多