在 Java 中无法使对象移动而不闪烁
Having trouble making object move without flickering in Java
我研究了双缓冲并计划最终实施它,但截至目前我还不知道如何使用它或类似的东西。我正在尝试制作乒乓球,所以我计划总共添加三个对象,但现在我只想让一个对象顺利运行。我对图形还很陌生,所以我不完全知道自己在做什么,我只是想边学边学。
这是我的代码:
傍:
public static void main(String[]args) {
JFrame window= new JFrame();
window.setTitle("Pong Game");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setPreferredSize(new Dimension(800,500));
window.pack();
window.setVisible(true);
Ball ball= new Ball();
Paddle player= new Paddle();
window.getContentPane().add(ball);
for(;;) {
ball.move();
//window.setContentPane(ball);
window.setContentPane(player);
player.move();
}
}
球拍:
double x, y, ymove;
boolean cpu;
public Paddle() {
x=5;
y=180;
ymove=.1;
}
//passing an integer through to make the computer paddle
public Paddle(int a) {
cpu= true;
x=761;
y=180;
ymove=.1;
}
public void paint(Graphics g) {
g.setColor(Color.blue);
g.fillRect((int)x, (int)y, 18, 120);
}
public void move() {
y+=ymove;
if(y>=500-160||y<=0) {
ymove*=-1;
}
}
球:
double x, y, xspeed, yspeed;
public Ball() {
x=200;
y=200;
xspeed=0;
yspeed=.1;
}
public void move() {
x+=xspeed;
y+=yspeed;
if(y>=440||y<=0) {
yspeed*=-1;
}
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.fillOval((int)x, (int)y, 20, 20);
}
您遇到的问题是您拆分了绘画方法,
如果你制作一个 class 专门用于绘画,并且你将所有的颜料都放在一种绘画方法中,它应该可以正常工作而不会闪烁。
我还建议考虑在计时器上进行绘画调用 运行,这样您就可以决定刷新率,这通常会带来更流畅的整体体验。
这是我最新游戏中的图形示例 class,
class GameGraphics extends JPanel implements ActionListener {
private Timer refreshHZTimer;
private int refreshHZ = 10;
private int frameID = 0;
public GameGraphics(int width, int height) {
setBounds(0,0, width, height);
setVisible(true);
refreshHZTimer = new Timer(refreshHZ, this);
refreshHZTimer.start();
}
@Override
public void actionPerformed(ActionEvent e) {
frameID++;
if (frameID % 100 == 1)
System.out.println("Painting FrameID: " + frameID);
repaint();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//Init graphics
Graphics2D g2 = (Graphics2D) g;
//Paint stuff here:
}
}
并将此代码添加到您的 JFrame 构造函数中:
GameGraphics gpu = new GameGraphics(width, height);
frame.add(gpu);
这个答案是一个非常非常简单的解释!我建议查看 this linux journal,它探索了类似的东西。
导致 "flickering" 的主要问题是平局已完成 "to fast"。
进入你的主循环:
for(;;) {
ball.move();
window.setContentPane(ball);
window.setContentPane(player);
player.move();
}
此循环更新球的位置,然后 "adds it to the content pane"。在绘制时,已经添加并绘制了下一张图像。这是导致闪烁的原因(再次注意:这非常简单)。
修复 "flickering" 的最简单解决方案是让线程在绘制完成后休眠 "wait" 直到绘制完成。
boolean running = true;
int delay = 15; // adjust the delay
while(running) {
ball.move();
player.move();
window.setContentPane(ball);
window.setContentPane(player);
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
// We were interrupted while waiting
// Something "woke us up". Stop the loop.
e.printStackTrace();
running = false;
}
}
此 Thread.sleep 方法让我们在指定时间使用当前 Thread "wait"。
延迟可以调整到更实用的程度。例如,您可以计算您想要多少帧并按该数量休眠。
另一种方法是 "time" 更新。这可以通过定时器来完成。由于它或多或少已被弃用,我使用 ScheduledExecutorService
实现它
int delay = 15; // adjust the delay
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool();
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
public void run() {
ball.move();
player.move();
}
}, delay, TimeUnit.MILLISECONDS)
从 Java8 开始,您可以像这样使用 Lambda 编写此代码:
scheduledExecutorService.scheduleAtFixedRate(() -> {
ball.move();
player.move();
}, delay, TimeUnit.MILLISECONDS)
要停止它,您现在可以调用:
scheduledExecutorService.shutdown();
但是:还有更复杂的解决方案。一个是,正如您已经指出的,double buffering. But there are also multiple different techniques,它补偿了更困难的问题。他们使用一种叫做翻页的东西。
我研究了双缓冲并计划最终实施它,但截至目前我还不知道如何使用它或类似的东西。我正在尝试制作乒乓球,所以我计划总共添加三个对象,但现在我只想让一个对象顺利运行。我对图形还很陌生,所以我不完全知道自己在做什么,我只是想边学边学。
这是我的代码:
傍:
public static void main(String[]args) {
JFrame window= new JFrame();
window.setTitle("Pong Game");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setPreferredSize(new Dimension(800,500));
window.pack();
window.setVisible(true);
Ball ball= new Ball();
Paddle player= new Paddle();
window.getContentPane().add(ball);
for(;;) {
ball.move();
//window.setContentPane(ball);
window.setContentPane(player);
player.move();
}
}
球拍:
double x, y, ymove;
boolean cpu;
public Paddle() {
x=5;
y=180;
ymove=.1;
}
//passing an integer through to make the computer paddle
public Paddle(int a) {
cpu= true;
x=761;
y=180;
ymove=.1;
}
public void paint(Graphics g) {
g.setColor(Color.blue);
g.fillRect((int)x, (int)y, 18, 120);
}
public void move() {
y+=ymove;
if(y>=500-160||y<=0) {
ymove*=-1;
}
}
球:
double x, y, xspeed, yspeed;
public Ball() {
x=200;
y=200;
xspeed=0;
yspeed=.1;
}
public void move() {
x+=xspeed;
y+=yspeed;
if(y>=440||y<=0) {
yspeed*=-1;
}
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.fillOval((int)x, (int)y, 20, 20);
}
您遇到的问题是您拆分了绘画方法, 如果你制作一个 class 专门用于绘画,并且你将所有的颜料都放在一种绘画方法中,它应该可以正常工作而不会闪烁。
我还建议考虑在计时器上进行绘画调用 运行,这样您就可以决定刷新率,这通常会带来更流畅的整体体验。
这是我最新游戏中的图形示例 class,
class GameGraphics extends JPanel implements ActionListener {
private Timer refreshHZTimer;
private int refreshHZ = 10;
private int frameID = 0;
public GameGraphics(int width, int height) {
setBounds(0,0, width, height);
setVisible(true);
refreshHZTimer = new Timer(refreshHZ, this);
refreshHZTimer.start();
}
@Override
public void actionPerformed(ActionEvent e) {
frameID++;
if (frameID % 100 == 1)
System.out.println("Painting FrameID: " + frameID);
repaint();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//Init graphics
Graphics2D g2 = (Graphics2D) g;
//Paint stuff here:
}
}
并将此代码添加到您的 JFrame 构造函数中:
GameGraphics gpu = new GameGraphics(width, height);
frame.add(gpu);
这个答案是一个非常非常简单的解释!我建议查看 this linux journal,它探索了类似的东西。
导致 "flickering" 的主要问题是平局已完成 "to fast"。
进入你的主循环:
for(;;) {
ball.move();
window.setContentPane(ball);
window.setContentPane(player);
player.move();
}
此循环更新球的位置,然后 "adds it to the content pane"。在绘制时,已经添加并绘制了下一张图像。这是导致闪烁的原因(再次注意:这非常简单)。
修复 "flickering" 的最简单解决方案是让线程在绘制完成后休眠 "wait" 直到绘制完成。
boolean running = true;
int delay = 15; // adjust the delay
while(running) {
ball.move();
player.move();
window.setContentPane(ball);
window.setContentPane(player);
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
// We were interrupted while waiting
// Something "woke us up". Stop the loop.
e.printStackTrace();
running = false;
}
}
此 Thread.sleep 方法让我们在指定时间使用当前 Thread "wait"。
延迟可以调整到更实用的程度。例如,您可以计算您想要多少帧并按该数量休眠。
另一种方法是 "time" 更新。这可以通过定时器来完成。由于它或多或少已被弃用,我使用 ScheduledExecutorService
实现它int delay = 15; // adjust the delay
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool();
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
public void run() {
ball.move();
player.move();
}
}, delay, TimeUnit.MILLISECONDS)
从 Java8 开始,您可以像这样使用 Lambda 编写此代码:
scheduledExecutorService.scheduleAtFixedRate(() -> {
ball.move();
player.move();
}, delay, TimeUnit.MILLISECONDS)
要停止它,您现在可以调用:
scheduledExecutorService.shutdown();
但是:还有更复杂的解决方案。一个是,正如您已经指出的,double buffering. But there are also multiple different techniques,它补偿了更困难的问题。他们使用一种叫做翻页的东西。