无法使对象移动而不会在Java中闪烁

时间:2018-12-18 19:47:42

标签: java graphics paint

我已经研究了Double Buffering,并计划最终实现它,但是到目前为止,我还不知道如何使用它或类似的东西。我正在尝试制作乒乓球,所以我计划总共添加三个对象,但现在我只想让一个对象平稳地工作。我对图形还很陌生,所以我完全不知道自己在做什么,我只是在尝试学习。

这是我的代码:

傍:

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);
}

2 个答案:

答案 0 :(得分:0)

您遇到的问题是您划分了绘画方法, 如果您要安排一门专门用于绘画的课,并且将所有绘画都放在一种绘画方法中,那么它应该不会闪烁。

我还建议您考虑使您的绘画调用在计时器上运行,该计时器可以让您确定刷新速度,通常可以使整体体验更加流畅。

这是我最新游戏中的图形类示例,

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);

答案 1 :(得分:0)

此答案是非常非常简单的解释!我建议您检查一下this linux journal,它会探索类似的东西。

导致“闪烁”的主要问题是抽奖是“快速”完成的。

进入主循环:

for(;;) {
    ball.move();
    window.setContentPane(ball);
    window.setContentPane(player);
    player.move();
}

此循环更新球的位置,然后“将其添加到内容窗格”。绘制时,已经添加并绘制了下一张图像。这导致闪烁(再次注意:这非常简化)。

最简单的解决“闪烁”的方法是,在绘制后让线程进入睡眠状态,并“等待”直到绘制完成。

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“等待”指定时间。

可以将延迟调整为更实际的值。例如,您可以计算所需的帧数并以该数量休眠。

另一种方法是“定时”更新。这可以用计时器来完成。由于它已或多或少被弃用,因此我使用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。但是也有多个different techniques,可以弥补更困难的问题。他们使用称为页面翻转的东西。