我有减少图像闪烁的问题

时间:2015-03-05 14:37:09

标签: java swing awt paint

闪烁并不总是发生,但是当它发生时(大约50%的时间)它非常糟糕。我尝试做了一些事情,最初我使用的是canvas,然后我尝试更改为jpanel并覆盖paintcomponent,但问题仍在继续。

我使用两种不同的方法进行绘画,我在主循环上调用(第一个绘制背景地形,第二个绘制顶部的图像),速度为60 fps。

这就是我创建Jpanel的方式:

private void createDisplay(){

    frame = new JFrame(title);   //linha mais importante, é criada uma JFrame com as propriedades definidas em baixo
    frame.setSize(width, height); //tamanho
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //linha necessário para que a operação acabe quando se sai da janela
    frame.setResizable(false); //remove a opção de alterar o tamanho da janela
    frame.setLocationRelativeTo(null); //definição da posição relativa, neste caso nula
    frame.setVisible(true); //torna a janela visivel

    canvas = new JPanel();


    //canvas = new Canvas(); //inicialização da canvas
    canvas.setPreferredSize(new Dimension(width, height)); //alteração do tamanho da canvas
    canvas.setMaximumSize(new Dimension(width, height));   //tamanho máximo e minimo são definidos para que a 
    canvas.setMinimumSize(new Dimension(width, height));   //canvas tenha sempre o mesmo tamanho

    frame.add(canvas);  //linha essencial, adicina a canvas á jframe

    frame.pack(); //necessário para que a canvas fique bemcentrada na frame (para que se veja toda a canvas)
    frame.addMouseListener(new MouseInput());
    canvas.addMouseListener(new MouseInput());


    }

这是我的主类循环的一部分,我调用了paint方法:

@Override
public void run() {

init();
Terrain.init();


int fps = 60;
double timePerTick = 1000000000 / fps;
double delta = 0;
long now;
long lastTime = System.nanoTime();
@SuppressWarnings("unused")
int ticks = 0;
long timer = 0;

while(running)  {

    now = System.nanoTime();        
    delta += (now - lastTime) / timePerTick;

    timer += now - lastTime;

    lastTime = now;

    if(delta >= 1){


gamepack.Terrain.paintComponent(g);

gamepack.Units.tick();

gamepack.Enemy.tick();


//gamepack.Enemy.render();




gamepack.Render.paintComponent(g);



ticks++;

delta--;

    }

    if(timer >= 1000000000){
        //System.out.println(ticks);  Verificação das actualizações (ticks e render por segundo)
        ticks = 0;
        timer = 0;
    }

 }
stop();
}    

这是地形的第一种绘画方法(它很长,我只包括第一部分):

public static void paintComponent(Graphics g) {  




  //  bs = Display.getCanvas().getBufferStrategy(); 

    //if(bs==null) {                                        
    //  Display.getCanvas().createBufferStrategy(3);  
    //  return;
    //}

    g = Display.getCanvas().getGraphics();                    

    g.clearRect(0, 0, 510, 510); 



    //Primeira Linha (1)
    g.drawImage(grasstopleftcorner ,0,0, null); //1                     
    g.drawImage(grasstopleftcorner ,34,0, null); //2
    g.drawImage(path ,68,0, null); //3
    g.drawImage(path ,102,0, null); //4
    g.drawImage(grasstopleftcorner ,136,0, null); //5

    g.drawImage(grasstopleftcorner ,170,0, null); //6
    g.drawImage(grasstopleftcorner ,204,0, null); //7
    g.drawImage(grasstopleftcorner ,238,0, null); //8
    g.drawImage(grasstopleftcorner ,272,0, null); //9
    g.drawImage(grasstopleftcorner ,306,0, null); //10

    g.drawImage(grasstopleftcorner ,340,0, null); //11
    g.drawImage(grasstopleftcorner ,374,0, null); //12
    g.drawImage(grasstopleftcorner ,408,0, null); //13
    g.drawImage(grasstopleftcorner ,442,0, null); //14
    g.drawImage(grasstopleftcorner ,476,0, null); //15

    //Segunda Linha (2)
    g.drawImage(grasstopleftcorner ,0,34, null); //1
    g.drawImage(grasstopleftcorner ,34,34, null); //2
    g.drawImage(path ,68,34, null); //3
    g.drawImage(path ,102,34, null); //4
    g.drawImage(grasstopleftcorner ,136,34, null); //5

    g.drawImage(grasstopleftcorner ,170,34, null); //6
    g.drawImage(grasstopleftcorner ,204,34, null); //7
    g.drawImage(grasstopleftcorner ,238,34, null); //8
    g.drawImage(grasstopleftcorner ,272,34, null); //9
    g.drawImage(grasstopleftcorner ,306,34, null); //10

    g.drawImage(grasstopleftcorner ,340,34, null); //11
    g.drawImage(grasstopleftcorner ,374,34, null); //12
    g.drawImage(grasstopleftcorner ,408,34, null); //13
    g.drawImage(grasstopleftcorner ,442,34, null); //14
    g.drawImage(grasstopleftcorner ,476,34, null); //15

这是在地形(或背景)上绘制图像的最后一种绘画方法:

public static void paintComponent(Graphics g){


//Código Comum a todas as Classes   
//bs = Display.getCanvas().getBufferStrategy(); 

//if(bs==null) {                                        
/// Display.getCanvas().createBufferStrategy(3);  
//  return;
//}

    g = Display.getCanvas().getGraphics();    
//g.clearRect(0, 0, 510, 510);
//Enemies
p=0;

//Este while serve para actualizar as imagens de todas as unidades ao incrementar o p, todas as variáveis do drawimage são actualizadas no tick()
while(p<Main.enemylist.size()){ 

    if(Main.enemylist.get(p).health > 0){



//  g.drawImage(Main.castleimg,(int) 100,(int)100, null);
    g.drawImage(Main.enemylist.get(p).usedimg.getSubimage(Main.enemylist.get(p).imgx*32, Main.enemylist.get(p).imgy*32 , 32, 32),(int) Main.enemylist.get(p).targetx - 16,(int) Main.enemylist.get(p).targety- 16, null);
    }
    p++;
}

    //Units
    p=0;

    //Este while serve para actualizar as imagens de todas as unidades ao incrementar o p, todas as variáveis do drawimage são actualizadas no tick()
    while(p<Main.dudelist.size()){ 

        if(Main.dudelist.get(p).selected){

            g.drawImage(Main.dotimage,(int) Main.dudelist.get(p).currentx + 7,(int) Main.dudelist.get(p).currenty - 19, null);

        }
        if (Main.dudelist.get(p).health > 0){

    //  g.drawImage(Main.castleimg,(int) 100,(int)100, null);
        g.drawImage(Main.dudelist.get(p).usedimg.getSubimage(Main.dudelist.get(p).imgx*32, Main.dudelist.get(p).imgy*32 , 32, 32),(int) Main.dudelist.get(p).currentx ,(int) Main.dudelist.get(p).currenty, null);
        }
        p++;

    }














g.dispose(); 

}

我希望我能很好地解释我的问题,并且我包含了所有需要的内容,对于java和编程来说还是一个新手,希望它不会让人感到困惑。

2 个答案:

答案 0 :(得分:1)

默认情况下,JPanel是双缓冲的,可防止闪烁。这依赖于唯一的Event Dispatching Thread进入的事件,然后调用paintComponent。此paintComponent也可以通过例如repaint(50L)手动(间接)调用。

动画可以通过Swing Timer完成,并定期调用方法。

这应该取代您的while循环。

这有点内而外,但不会处理所有的长循环。一个人编写单独的事件处理程序,例如在按键时可以增加speed计数器,等等。

在paintComponent中,不需要clear(通常)。其余的优化是创造性的问题。例如,.png图像可以具有透明度,因此您可以制作整个带孔的帧图像。

答案 1 :(得分:1)

g = Display.getCanvas().getGraphics();     

不要使用getGraphics()方法。

paintComponent()方法已经有了一个Graphics对象。这是你应该用于绘画的对象。

您还应该:     super.paintComponent方法(克);

在方法的顶部清除面板的背景。这比使用带有硬编码值的clearRect(...)方法更好。