所以通常当我制作模拟程序(比如这个)时,我会寻找可以改善的情况,以防情况再次发生。
今天我以为我会用基本的OOP(我理解OOP的概念,只是避开了它并且想要清新我的记忆)。所以我决定做一个只在10x10平面和1个玩家(你)上创造3个怪物的小游戏,你可以在任何x / y方向移动你的玩家。我的计划有效,但我无法帮助,但觉得我做错了。
所以我的程序的基本布局是有5个类。一个GUI类,显示游戏并为您提供移动控制的方向按钮,创建怪物的类,创建玩家的类,创建10x10板并跟踪怪物/玩家位置的类,当然还有主类创建所有对象并具有主游戏循环和诸如此类的东西。
我在与主类和GUI类交互时遇到了一些困难。我最后做的是在我的主类中做一个while循环并等待玩家按下开始按钮,一旦玩家按下它(通过动作监听器),GUI类将公共变量(运行)从false设置为true,一旦变量发生变化,我就可以采取相应的行动。
这里我觉得我做错了什么:首先我的while循环不会终止,除非我打印到控制台。我用谷歌搜索了这个问题,显然人们已经说过线程或“活跃的民意调查”这个问题,我不明白。我去了我的程序并在我的while循环中添加了一个小的10ms线程睡眠,一切都开始工作了。
我向你们提问的是,什么是积极的民意调查?为什么不好?在我的程序中如何/为什么/这是怎么回事?最后,如果有更好的方式与GUI类和主类进行交互。对不起文字的巨大墙壁,但我想在解释情况时要彻底!
TL; DR:我是否正确地与GUI类和主类进行交互?如果不是这样做的正确方法是什么?
我的主要课程:
public class MainGame {
public static void main(String[] args) throws InterruptedException{
ShowGUI gui = new ShowGUI();
while(!gui.running){
Thread.sleep(10);
}
Board gameBoard = new Board();
gui.setLabelText(gameBoard.getBoard());
//Add Player
Player playerOne = new Player(1000, "Player 1");
//Add monsters
Monster monstMatt = new Monster(1000, "Matt");
Monster monstJon = new Monster(1000, "Jon");
Monster monstAbaad = new Monster(1000, "Abaad");
while(gui.running){
Thread.sleep(10);
int x, y;
x = playerOne.getX();
y = playerOne.getY();
if(gui.buttonPress != -1){
if(gui.buttonPress == 1){
playerOne.move(x, --y);
}else if(gui.buttonPress == 2){
playerOne.move(x, ++y);
}else if(gui.buttonPress == 3){
playerOne.move(--x, y);
}else if(gui.buttonPress == 4){
playerOne.move(++x, y);
}
gui.buttonPress = -1;
gui.setLabelText(gameBoard.getBoard());
}
}
}
}
我的GUI类:
public class ShowGUI{
private JTextArea board;
private JButton moveUp;
private JButton moveDown;
private JButton moveLeft;
private JButton moveRight;
public boolean running = false;
public int buttonPress = -1;
public ShowGUI(){
System.out.println("GUI Successfully Loaded");
createAndShow();
}
private void createAndShow(){
JFrame mainFrame = new JFrame("Bad Game");
addComponents(mainFrame.getContentPane());
mainFrame.setSize(500, 400);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setLocationRelativeTo(null);
mainFrame.setResizable(false);
mainFrame.setVisible(true);
}
private void addComponents(Container pane){
pane.setLayout(null);
board = new JTextArea(1, JLabel.CENTER);
moveUp = new JButton("Up");
moveDown = new JButton("Down");
moveLeft = new JButton("Left");
moveRight = new JButton("Right");
moveUp.setBounds(185, 225, 130, 35);
moveLeft.setBounds(115, 280, 130, 35);
moveRight.setBounds(255, 280, 130, 35);
moveDown.setBounds(185, 335, 130, 35);
board.setEditable(false);
board.setBounds(115, 30, 270, 145);
board.setFont(new Font("Consolas", Font.BOLD, 12));
addActionListeners();
pane.add(board);
pane.add(moveUp);
pane.add(moveRight);
pane.add(moveLeft);
pane.add(moveDown);
}
private void addActionListeners(){
moveUp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
running = true;
buttonPress = 1;
}
});
moveDown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPress = 2;
}
});
moveLeft.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPress = 3;
}
});
moveRight.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
buttonPress = 4;
}
});
}
public void setLabelText(char[][] boardToShow){
board.setText(" ");
for(int i = 0; i < boardToShow.length; i++){
for(int j = 0; j < boardToShow[i].length; j++){
board.append(boardToShow[i][j] + " ");
}
board.append("\n ");
}
}
}
如果您需要我的Board / Monster / Player课程,我可以发布,但我不认为这些课程存在问题。
答案 0 :(得分:0)
主动民意调查(又名busy waiting)是指一个程序反复检查某些条件是否属实,而且由于我们正在谈论计算机,这通常会很多一秒钟。这很糟糕,因为这意味着进程不断耗尽CPU来检查这种情况,更糟糕的是,因为它占用了大量的CPU,它可以防止条件成为真正的第一名(这就是你的情况)。
我试着用一个比喻解释一下,你的Main类(特别是你的while(gui.running)
循环)是老板,GUI类是员工。想象一下,老板每秒都会来找他的员工并问&#34;你做过我要求你做的事吗?&#34; 。通过这样做每一秒,不仅老板浪费他的时间,而且实际上是在阻止他的员工做他被要求做的事情。
这正是您的Main类和GUI类之间发生的事情。当buttonPress
循环继续运行时while
的值不能改变,这就是为什么要睡觉(以及打印到控制台,因为这需要一个IO操作,这也会阻塞线程一段时间) )使它工作,因为while
循环停止执行很短的时间,让你的程序有机会改变buttonPress
的值。
解决方案
为了解决这个问题,让我们回到老板/员工的比喻中。老板应该说,而不是每秒检查,&#34;当你完成这件事时,来告诉我&#34; 。这样他就不需要继续检查员工,员工有时间完成任务。同样,当actionListeners
被触发时,您的GUI类应该调用Main类中的方法。最终,正如其他人所指出的那样,你的结构需要相当多的工作和清理,我建议你考虑使用Model-View-Controller模式。
但是,我会提出一个适用于您目前设置的解决方案。您的主要课程应该类似于:
public class Main {
private Player playerOne;
private Monster monstMatt;
private Monster monstJon;
private Monster monstAbaad;
private ShowGUI gui;
private Board gameBoard;
public Main() {
//Add Player
playerOne = new Player(1000, "Player 1");
//Add monsters
monstMatt = new Monster(1000, "Matt");
monstJon = new Monster(1000, "Jon");
monstAbaad = new Monster(1000, "Abaad");
gui = new ShowGUI(this);
gameBoard = new Board();
gui.setLabelText(gameBoard.getBoard());
}
public movePlayerUp() {
movePlayer(0, -1);
}
public movePlayerDown() {
movePlayer(0, 1);
}
public movePlayerLeft() {
movePlayer(-1, 0);
}
public movePlayerRight() {
movePlayer(1, 0);
}
private movePlayer(x, y) {
playerOne.move(playerOne.getX() + x, playerOne.getY() + y);
}
}
GUI类:
public class ShowGUI {
private Main main;
public ShowGui(Main main) {
this.main = main;
createAndShow();
System.out.println("GUI Successfully Loaded");
}
private void addActionListeners(){
moveUp.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerUp();
}
});
moveDown.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerDown();
}
});
moveLeft.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerLeft();
}
});
moveRight.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
main.movePlayerRight();
}
});
}
/* All the other methods you have */
...
}
因此,不使用标志(buttonPress=2
),而是使用在发生特定操作时将调用的方法。同样,这不是一个完美的长期解决方案,但它有正确的要点,是你应该遵循的那种模式。