Swing需要createBufferStrategy(2)才能正确绘制

时间:2014-12-06 16:49:01

标签: java swing jframe bufferstrategy

我有一些被逐屏绘制的网格。我使用箭头键将网格作为一组移动。据说Swing默认是doubleBuffered所以我认为frame.createBufferStrategy(2)是一个不好的做法,但问题是当我不使用手动双缓冲时,网格未对齐并且它们之间出现了一些洞。使用手动双缓冲修复它。

我在实际程序中遇到了一些图形问题(例如对话框的按钮无法正常显示)(不在SSCCE中),所以我认为这可能是由于双缓冲的错误实现造成的。

这是该程序的SSCCE,当不手动双缓冲时会导致网格错位:

package SSCCE;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main {
boolean manuallyDoubleBuffered = false; //change this

static Main main;

public final JFrame frame = new JFrame();
public final Keys keys = new Keys();
private JPanel panel;
private BufferStrategy bufferStrategy;



public static void main(String[] args) {
    main = new Main();
    main.initiate();
    // --START LOOP--
    Thread loop = new Thread(main.new Looper());
    loop.start();
}

public void initiate() {
    frameInit();
    keys.start();
}

private void frameInit() {
    frame.setSize(1200, 750);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
    setUpGUI();
    if (manuallyDoubleBuffered)
        frame.createBufferStrategy(2); // manual double buffering
    bufferStrategy = frame.getBufferStrategy();
}

private void setUpGUI() {
    panel = new JPanel() {
        @Override
        protected void paintComponent(Graphics g) {
            Graphics2D g2d = (Graphics2D) g;
            Main.main.rendering(g2d);
            super.paintComponent(g);
        }
    };

    LayoutManager layout = new FlowLayout();
    frame.getContentPane().setBackground(Color.black);
    panel.setLayout(layout);
    panel.setOpaque(false);//
    JButton but1 = new JButton("but1");
    panel.add(but1);
    frame.add(panel);
}

class Looper implements Runnable {
    @Override
    public void run() {
        Main.main.gameLoop();
    }
}

private void gameLoop() {
    // variables are declared at start
    while (true) {

        if (manuallyDoubleBuffered)
            paint(); // MANUAL double buffering
        else
            frame.repaint();// no manual double buffering

        update();

        try {
            Thread.sleep(1000 / 60);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}// loop end

private void update() {
    move();

}

private void rendering(Graphics2D g2d) {
    // // testing
    paintGrids(g2d);
}

private void move() {
    x += sx;
    y += sy;
}

int sx = 0; //speedX
int sy = 0; //speedY

//top left corner of the grid
int x = 0; 
int y = 0;

private void paintGrids(Graphics2D g) {
    for (int i = 0; i < 100; i++) {
        for (int t = 0; t < 100; t++) {
            g.setColor(Color.GRAY);
            g.fillRect(i * 50 + x, t * 50 + y, 50, 50);
            g.setColor(Color.BLACK);
            g.drawString(i + "," + t, i * 50 + x, t * 50 + y + 10);
        }
    }

}

public void paint() {
    // uses double buffering system.
    do {
        do {
            Graphics2D g2d = (Graphics2D) bufferStrategy.getDrawGraphics();
            g2d.fillRect(0, 0, frame.getWidth(), frame.getHeight());

            try {
                frame.paint(g2d);
            } catch (NullPointerException e) {
                e.printStackTrace();
            }
            g2d.dispose();
        } while (bufferStrategy.contentsRestored());
        bufferStrategy.show();
    } while (bufferStrategy.contentsLost());
}
}

class Keys implements KeyListener {// Trimmed down to shorten SSCCE
private final int leftKey = 37; // left b.
private final int rightKey = 39; // Right b.
private final int upKey = 38;// up k.
private final int downKey = 40;// down k.

public void start() {

    Main.main.frame.addKeyListener(this);
    Main.main.frame.setFocusable(true);
}

private void left() {
    Main.main.sx -= 10;
}

private void right() {
    Main.main.sx += 10;
}

private void up() {
    Main.main.sy -= 10;
}

private void down() {
    Main.main.sy += 10;
}

@Override
public void keyPressed(KeyEvent e) {
    // TODO Auto-generated method stub
    System.out.println(e.getKeyCode());
    switch (e.getKeyCode()) {
    case leftKey:
        left();
        break;
    case rightKey:
        right();
        break;
    case downKey:
        down();
        break;
    case upKey:
        up();
        break;
    }
}

@Override
public void keyReleased(KeyEvent arg0) {
    // TODO Auto-generated method stub

}

@Override
public void keyTyped(KeyEvent arg0) {
    // TODO Auto-generated method stub

}

}// END OF THE KEYS CLASS

Oracle的swing教程并没有解释游戏循环的用法。最好的方法是什么?我做错了吗?

如果在其他计算机上没有再现视觉错误,我正在上传截图: Those black lines are not supposed to happen. 黑线是由矩形的错误引起的。当手动双缓冲设置为true时,它们不存在。

提前致谢。

编辑:我忘了提到网格移动时会出现黑线。

我也发现,手动双缓冲会大大降低性能。

编辑2:我已经修复了问题,并将其作为答案发布,但随时可以对我的代码发表评论。主类(gameLoop除外)类似于我在程序中使用的实际主类。

2 个答案:

答案 0 :(得分:1)

我看不到后台的任何变化。这是我做的代码更改。

public static void main(String[] args) {
    main = new Main();

    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            main.initiate();
        }           
    });

    // --START LOOP--
    Thread loop = new Thread(main.new Looper());
    loop.start();
}

必须始终通过调用SwingUtilities.invokeLater启动Swing应用程序。

答案 1 :(得分:0)

我已经找到了问题并写在这里以防万一其他任何事情发生。

问题是由于程序是多线程的。网格的左上角坐标(x和y)由paintGrids()方法中间的另一个线程更新。手动双缓冲正在减慢程序速度(减少数百次),这使得paintGrids方法可以在按键更新x和y之前完成绘制。

要修复它,我已将以下内容添加到paintGrids方法的开头:

int x = this.x;
int y = this.y;