I try to write a game of Air Hockey, I think the essence of the game is clear. I have three classes, this is the main window, the playing field and the statistical field. The movement of the puck begins after pressing the button from the Statistical field, is carried out by redrawing in the timer handler.

Question one: Before pressing this button, players can move (in the main class), but not simultaneously. I understand the problem in the streams (

Question two: After pressing the button, the puck begins its movement and the players no longer move. Here, as I understand it, the problem is also in the streams (I can’t understand how to do it right. Like everything here, I'm just learning, don't strictly judge the code.

Main class:

package com.company; import javax.swing.*; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import static java.awt.event.KeyEvent.*; public class MainWindow { public MainWindow() { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); // Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ Ρ„Ρ€Π΅ΠΉΠΌΠ° JFrame jFrame = new JFrame(); jFrame.setLocation(screenSize.width / 2 - (StatisticField.STATISTIC_FIELD_W + GameField.GAME_FIELD_W) / 2, screenSize.height / 2 - StatisticField.STATISTIC_FIELD_H / 2); jFrame.setTitle("Air Hockey"); jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); jFrame.setVisible(true); jFrame.setLayout(new FlowLayout()); jFrame.setResizable(false); jFrame.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { super.keyPressed(e); if (e.getKeyCode() == VK_UP) { GameField.setPlayer2Y(GameField.getPlayer2Y() - 5); jFrame.repaint(5); } if (e.getKeyCode() == VK_DOWN) { GameField.setPlayer2Y(GameField.getPlayer2Y() + 5); jFrame.repaint(5); } if (e.getKeyCode() == VK_W) { GameField.setPlayer1Y(GameField.getPlayer1Y() - 5); jFrame.repaint(5); } if (e.getKeyCode() == VK_S) { GameField.setPlayer1Y(GameField.getPlayer1Y() + 5); jFrame.repaint(5); } } }); // Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΈΠ³Ρ€ΠΎΠ²ΠΎΠ³ΠΎ ΠΈ статистичСского поля StatisticField statisticField = new StatisticField(); GameField gameField = new GameField(); statisticField.setPreferredSize(new Dimension(StatisticField.STATISTIC_FIELD_W, StatisticField.STATISTIC_FIELD_H)); gameField.setPreferredSize(new Dimension(GameField.GAME_FIELD_W, GameField.GAME_FIELD_H)); jFrame.add(gameField); jFrame.add(statisticField); jFrame.pack(); } public static void main(String[] args) { MainWindow mainWindow = new MainWindow(); } } 

Playground class:

 package com.company; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; public class GameField extends JPanel implements ActionListener { public static final int WIN_POINTS = 7; // ΠžΡ‡ΠΊΠΎΠ² для ΠΏΠΎΠ±Π΅Π΄Ρ‹ public static int GAME_FIELD_H = 400; // Высота ΠΈΠ³Ρ€ΠΎΠ²ΠΎΠ³ΠΎ поля public final static int GAME_FIELD_W = 700; // Π¨ΠΈΡ€ΠΈΠ½Π° ΠΈΠ³Ρ€ΠΎΠ²ΠΎΠ³ΠΎ поля private final static int PUCK_SIZE = 20; // Π Π°Π·ΠΌΠ΅Ρ€ ΡˆΠ°ΠΉΠ±Ρ‹ private final static int PLAYER_HEIGHT = 100; // Высота ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ² private final static int PLAYER_WIDTH = 15; // Π¨ΠΈΡ€ΠΈΠ½Π° ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ² private int puckX; // Π₯-ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π° ΡˆΠ°ΠΉΠ±Ρ‹ private int puckY; // Π£-ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π° ΡˆΠ°ΠΉΠ±Ρ‹ private static int player1X; // Π₯-ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π° 1-Π³ΠΎ ΠΈΠ³Ρ€ΠΎΠΊΠ° private static int player1Y; // Π£-ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π° 1-Π³ΠΎ ΠΈΠ³Ρ€ΠΎΠΊΠ° private static int player2X; // Π₯-ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π° 2-Π³ΠΎ ΠΈΠ³Ρ€ΠΎΠΊΠ° private static int player2Y; // Π£-ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π° 2-Π³ΠΎ ΠΈΠ³Ρ€ΠΎΠΊΠ° private static int player1Points; // ΠžΡ‡ΠΊΠΈ 1-Π³ΠΎ ΠΈΠ³Ρ€ΠΎΠΊΠ° private static int player2Points; // ΠžΡ‡ΠΊΠΈ 2-Π³ΠΎ ΠΈΠ³Ρ€ΠΎΠΊΠ° private Timer timerPuck; // ВрСмя пСрСрисовки private boolean puckUp; // НаправлСниС двиТСния ΡˆΠ°ΠΉΠ±Ρ‹ private boolean puckDown; private boolean puckLeft; private boolean puckRight; private static boolean player1Up = false; // НаправлСниС двиТСния ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ² private static boolean player1Down = false; private static boolean player2Up = false; private static boolean player2Down = false; private static boolean inGame = false; private int route; // ΠΠ°Ρ‡Π°Π»ΡŒΠ½ΠΎΠ΅ Π½Π°ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ двиТСния ΡˆΠ°ΠΉΠ±Ρ‹ public static int getPlayer1Y() { return player1Y; } public static void setPlayer1Y(int player1Y) { GameField.player1Y = player1Y; } public static int getPlayer2Y() { return player2Y; } public static void setPlayer2Y(int player2Y) { GameField.player2Y = player2Y; } public static boolean isInGame() { return inGame; } public static void setInGame(boolean inGame) { GameField.inGame = inGame; } // ΠžΡ‚Ρ€ΠΈΡΠΎΠ²ΠΊΠ° ΡˆΠ°ΠΉΠ±Ρ‹ ΠΈ ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ² @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g; Rectangle2D player1 = new Rectangle2D.Double(player1X, player1Y, PLAYER_WIDTH, PLAYER_HEIGHT); Rectangle2D player2 = new Rectangle2D.Double(player2X, player2Y, PLAYER_WIDTH, PLAYER_HEIGHT); Ellipse2D puck = new Ellipse2D.Double(puckX, puckY, PUCK_SIZE, PUCK_SIZE); g2.setColor(Color.RED); g2.fill(puck); g2.setColor(Color.BLACK); g2.fill(player1); g2.fill(player2); } // Π”Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ ΡˆΠ°ΠΉΠ±Ρ‹ public void movePuck() { if(puckUp) puckY -= 1; if(puckDown) puckY += 1; if(puckLeft) puckX -= 1; if(puckRight) puckX += 1; } // Π’Ρ‹Π±ΠΎΡ€ Π½Π°Ρ‡Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ направлСния двиТСния ΡˆΠ°ΠΉΠ±Ρ‹ public void choiceOfDirection(){ route = (int)(Math.random() * 3); if (route == 0) { puckRight = true; puckUp = true; puckLeft = false; puckDown = false; } if (route == 1) { puckRight = false; puckUp = true; puckLeft = true; puckDown = false; } if (route == 2) { puckRight = false; puckUp = false; puckLeft = true; puckDown = true; } if (route == 3) { puckRight = true; puckUp = false; puckLeft = false; puckDown = true; } } // Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ ΠΈΠ³Ρ€Ρ‹ public void initGame(){ player1Points = 0; player2Points = 0; choiceOfDirection(); // ΠΠ°Ρ‡Π°Π»ΡŒΠ½Ρ‹Π΅ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ ΡˆΠ°ΠΉΠ±Ρ‹ ΠΈ ΠΈΠ³Ρ€ΠΎΠΊΠΎΠ² puckX = GAME_FIELD_W/2 - PUCK_SIZE/2; puckY = GAME_FIELD_H/2 - PUCK_SIZE/2; player1X = 0; player1Y = GAME_FIELD_H/2 - PLAYER_HEIGHT/2; player2X = GAME_FIELD_W - PLAYER_WIDTH; player2Y = GAME_FIELD_H/2 - PLAYER_HEIGHT/2; timerPuck = new Timer(1, this); timerPuck.start(); } public GameField() { // Установка Ρ€Π°ΠΌΠΊΠΈ setBorder(BorderFactory.createCompoundBorder( BorderFactory.createRaisedBevelBorder(), BorderFactory.createLoweredBevelBorder())); // Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ ΠΈΠ³Ρ€Ρ‹ Π² конструкторС initGame(); setFocusable(true); } public void playerWin(Label player){ JOptionPane.showMessageDialog( this, player.getText() + " WIN!!!", "Congratulations", JOptionPane.PLAIN_MESSAGE); StatisticField.getStart().setLabel("Start Game"); initGame(); StatisticField.getPoint1().setText("0"); StatisticField.getPoint2().setText("0"); } // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π½Π° столкновСниС ΡˆΠ°ΠΉΠ±Ρ‹ с Π±ΠΎΡ€Ρ‚Π°ΠΌΠΈ ΠΈ Π½Π° Π³ΠΎΠ» public void checkBorder(){ if(puckY == 0){ puckDown = true; puckUp = false; } if(puckY == GAME_FIELD_H - PUCK_SIZE){ puckDown = false; puckUp = true; } if(puckX == PLAYER_WIDTH && puckY + PUCK_SIZE/2 >= player1Y && puckY + PUCK_SIZE/2 <= player1Y + PLAYER_HEIGHT){ puckRight = true; puckLeft = false; } if(puckX == -PUCK_SIZE){ player2Points++; inGame = false; choiceOfDirection(); puckX = GAME_FIELD_W/2 - PUCK_SIZE/2; puckY = GAME_FIELD_H/2 - PUCK_SIZE/2; StatisticField.getPoint2().setText("" + player2Points); if(player2Points == WIN_POINTS){ playerWin(StatisticField.getPlayer2Name()); } } if(puckX == GAME_FIELD_W - PUCK_SIZE - PLAYER_WIDTH && puckY + PUCK_SIZE/2 >= player2Y && puckY + PUCK_SIZE/2 <= player2Y + PLAYER_HEIGHT){ puckLeft = true; puckRight = false; } if(puckX == GAME_FIELD_W + PUCK_SIZE){ player1Points++; inGame = false; choiceOfDirection(); puckX = GAME_FIELD_W/2 - PUCK_SIZE/2; puckY = GAME_FIELD_H/2 - PUCK_SIZE/2; StatisticField.getPoint1().setText("" + player1Points); if(player1Points == WIN_POINTS){ playerWin(StatisticField.getPlayer1Name()); } } } // Π‘ΠΎΠ±Ρ‹Ρ‚ΠΈΠ΅ Ρ‚Π°ΠΉΠΌΠ΅Ρ€Π° @Override public void actionPerformed(ActionEvent e) { if(inGame) { movePuck(); checkBorder(); repaint(); } } } 

Class statistical field:

 package com.company; import javax.swing.*; import javax.swing.border.Border; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class StatisticField extends JPanel { public final static int STATISTIC_FIELD_H = 400; // Высота статистичСского поля public final static int STATISTIC_FIELD_W = 200; // Π¨ΠΈΡ€ΠΈΠ½Π° статистичСского поля // ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ private static Label player1Name = new Label("Player 1", Label.CENTER); private static Label player2Name = new Label("Player 2", Label.CENTER); private TextField player1NameTextField = new TextField(); private TextField player2NameTextField = new TextField(); private static Label point1 = new Label("0", Label.CENTER); private static Label point2 = new Label("0", Label.CENTER); private static Button start = new Button("Start Game"); public static Label getPoint1() { return point1; } public static Label getPoint2() { return point2; } public static Label getPlayer1Name() { return player1Name; } public static Label getPlayer2Name() { return player2Name; } public static Button getStart() { return start; } public StatisticField(){ // Π¨Ρ€ΠΈΡ„Ρ‚ счСта Font font = new Font("TimesRoman", Font.BOLD, 100); // ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ JPanel settingPanel = new JPanel(); JPanel setting1player = new JPanel(); JPanel setting2player = new JPanel(); JPanel points = new JPanel(); Label colon = new Label(":", Label.CENTER); Border border = BorderFactory.createCompoundBorder( BorderFactory.createRaisedBevelBorder(), BorderFactory.createLoweredBevelBorder()); Label setting1Up = new Label("\"W\" - UP", Label.CENTER); Label setting2Up = new Label("\"UP\" - UP", Label.CENTER); Label setting1Down = new Label("\"S\" - DOWN", Label.CENTER); Label setting2Down = new Label("\"DOWN\" - DOWN", Label.CENTER); Button renamePlayer1 = new Button("Rename"); Button renamePlayer2 = new Button("Rename"); // Установка ΠΌΠ΅Π½Π΅Π΄ΠΆΠ΅Ρ€ΠΎΠ² ΠΊΠΎΠΌΠΏΠ°Π½ΠΎΠ²ΠΊΠΈ setLayout(new GridLayout(3, 1)); setting1player.setLayout(new GridLayout(5, 1)); setting2player.setLayout(new GridLayout(5, 1)); settingPanel.setLayout(new GridLayout(1, 2)); points.setLayout(new GridLayout(1, 3)); // Установка Ρ€Π°ΠΌΠΎΠΊ settingPanel.setBorder(border); points.setBorder(border); // Установка ΡˆΡ€ΠΈΡ„Ρ‚Π° point1.setFont(font); point2.setFont(font); colon.setFont(font); // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° панСль points points.add(point1); points.add(colon); points.add(point2); // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° панСль setting1player setting1player.add(player1Name); setting1player.add(setting1Up); setting1player.add(setting1Down); setting1player.add(player1NameTextField); setting1player.add(renamePlayer1); // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° панСль setting2player setting2player.add(player2Name); setting2player.add(setting2Up); setting2player.add(setting2Down); setting2player.add(player2NameTextField); setting2player.add(renamePlayer2); // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° панСль settingPanel settingPanel.add(setting1player); settingPanel.add(setting2player); // Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° статистичСскоС ΠΏΠΎΠ»Π΅ add(points); add(settingPanel); add(start); // ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΊΠ½ΠΎΠΏΠΊΠΈ start start.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { start.setLabel("Continue Game"); GameField.setInGame(true); } }); // ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΊΠ½ΠΎΠΏΠΊΠΈ renamePlayer1 renamePlayer1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(!player1NameTextField.getText().equals("")) { player1Name.setText(player1NameTextField.getText()); player1NameTextField.setText(""); } } }); // ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ ΠΊΠ½ΠΎΠΏΠΊΠΈ renamePlayer2 renamePlayer2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if(!player2NameTextField.getText().equals("")) { player2Name.setText(player2NameTextField.getText()); player2NameTextField.setText(""); } } }); } } 
  • Show all already! class statistical field - in the studio - Oleksiy Mororets
  • There is no need to create streams for players. All the work of your streams is in CREATING AND ADDING listeners. Everything. The flow has stopped its work. And the non-simultaneity of control, I think, is related to how the keys are processed. Obviously, this way it is impossible to track the simultaneous key presses. - Olexiy Morenets
  • Yes, the fact that the streams do not work as I planned, I understood, I will correct. Give a tip on how to ensure simultaneous movements and players and pucks - chupa
  • No need to create multiple streams for leafers, since the keyboard is still the same. Try to implement the MVC pattern. - Drakonoved
  • Thanks for the comments and advice, I will try) - chupa

0