Основная проблема в том, что вы нарушаете базовую систему рисования Swing.Swing использует алгоритм «пассивного рендеринга», где рисование выполняется только тогда, когда это необходимо.Вы можете вносить предложения в API о том, когда что-то должно быть обновлено с помощью вызова repaint
.
Основываясь на вашем коде, основная проблема заключается в том, что вы звоните paintComponents
с вашим собственным Graphics
контекстом, но система затем удаляет его с помощью прохода краски, вы боретесь с системой рисования, а не работаете с ней.
Если все сделано правильно, компоненты Swing уже имеют двойную буферизацию, поэтому вам не нужночтобы сделать что-то «лишнее», кроме реальной работы с API / системой.
Я настоятельно рекомендую взглянуть на:
, чтобы лучше понять, как рисование работает в Swing.
Такдавайте начнем с ...
@Override
public void run() {
while (!isGameOver) {
dbImage = createImage(screenWidth, screenHeight);
dbg = this.getGraphics();
if (!isPaused) {
if (!gameOverCheck()) {
updateGame();
paintComponents(dbg);
}
} else if (isPaused) {
dbg.setColor(Color.ORANGE);
dbg.setFont(new Font("serif", Font.BOLD, 50));
dbg.drawString("Paused", screenWidth / 2 - 82, screenHeight / 2);
}
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- Swing НЕ является потокобезопасным, вы НЕ должны обновлять пользовательский интерфейс вне контекста нити диспетчеризации событий
- Вы не должны НИКОГДАвызовите любой метод
paint
напрямую.Система выполнит эту операцию, когда захочет обновить ваш компонент.
Я настоятельно рекомендую прочитать:
Например ...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.text.Position;
public class PongPanel extends JPanel implements Runnable {
private int screenWidth = 500;
private int screenHeight = 300;
private boolean isPaused = false;
private boolean isGameOver = false;
private int playToPoints = 10;
private Padel player1, player2;
private Ball ball;
private Timer gameThread;
public PongPanel() {
setPreferredSize(new Dimension(screenWidth, screenHeight));
setBackground(Color.BLACK);
setDoubleBuffered(true);
setFocusable(true);
requestFocus();
player1 = new Padel(Position.LEFT, screenWidth, screenHeight);
player2 = new Padel(Position.RIGHT, screenWidth, screenHeight);
ball = new Ball(10, screenWidth / 2, screenHeight / 2, Color.WHITE);
}
public void addNotify() {
super.addNotify();
startGame();
}
private void startGame() {
gameThread = new Timer(30, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateGame();
gameOverCheck();
if (isGameOver) {
repaint();
return;
}
if (!isPaused) {
if (!gameOverCheck()) {
updateGame();
}
}
repaint();
}
});
gameThread.start();
}
private boolean gameOverCheck() {
if (player1.getScore() == playToPoints) {
setGameOver(true);
return true;
} else if (player2.getScore() == playToPoints) {
setGameOver(true);
return true;
}
return false;
}
private void updateGame() {
ball.move(screenWidth, screenHeight, player1, player2);
player1.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY());
player2.aiForPadel(screenWidth, screenHeight, ball.getX(), ball.getY());
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLACK);
g2d.fillRect(0, 0, screenWidth + 20, screenHeight + 20);
g2d.setColor(Color.WHITE);
g2d.drawLine(screenWidth / 2, 0, screenWidth / 2, screenHeight);
g2d.setFont(new Font("serif", Font.BOLD, 32));
g2d.drawString(player1.getScore() + "", screenWidth / 2 - 40, screenHeight - 20);
g2d.drawString(player2.getScore() + "", screenWidth / 2 + 20, screenHeight - 20);
ball.drawBall(g2d);
player1.drawPadel(g2d);
player2.drawPadel(g2d);
if (isGameOver) {
if (player1.getScore() == playToPoints) {
g2d.setColor(player1.getColour());
g2d.setFont(new Font("serif", Font.BOLD, 50));
g2d.drawString("Player 1 Wins!", screenWidth / 2 - 161, screenHeight / 2);
} else if (player2.getScore() == playToPoints) {
g2d.setColor(player2.getColour());
g2d.setFont(new Font("serif", Font.BOLD, 50));
g2d.drawString("Player 2 Wins!", screenWidth / 2 - 161, screenHeight / 2);
}
} else if (isPaused) {
g2d.setColor(Color.ORANGE);
g2d.setFont(new Font("serif", Font.BOLD, 50));
g2d.drawString("Paused", screenWidth / 2 - 82, screenHeight / 2);
}
g2d.dispose();
}
}
BufferedStrategy
Уже предлагалось, BufferStrategy
- жизнеспособное решение в тех случаях, когда вы хотите полностью контролировать систему окраски.Это сложнее, но позволяет разобраться в странностях пассивной системы рендеринга, используемой Swing