我在减少图像闪烁时遇到问题

Posted

技术标签:

【中文标题】我在减少图像闪烁时遇到问题【英文标题】:Im having problems reducing image flickering 【发布时间】:2015-05-06 23:20:22 【问题描述】:

闪烁并不总是发生,但当它发生时(大约 50% 的时间)它非常糟糕。我尝试做一些事情,最初我使用的是画布,然后我尝试更改为 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());


    

这是我调用绘制方法的主类循环的一部分:

@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 和编程相当陌生,希望它不会很混乱。

【问题讨论】:

1) 为了尽快获得更好的帮助,请发布MCVE(最小完整可验证示例)或SSCCE(简短、自包含、正确示例)。 2) 例如,获取图像的一种方法是热链接到在this Q&A 中看到的图像。 进一步提示:1) 使用逻辑一致的缩进代码行和块的形式。缩进是为了让代码流更容易理解! 2) 曾经只需要源代码中的一个空白行。 之后或 之前的空行通常也是多余的。 3) static 不是你的朋友。它经常破坏而不是修复它们。 看起来您的绘画方法中绘制了大量图像。也许您可以尝试减少绘制的图像数量,如果您将 4 个相邻的图像合二为一,那将大大降低对性能的影响。或者,如果那不可能,你让 java 构建一个当前的大图像,包括所有小图像,并绘制这个更大的图像。 (只是一个想法,不知道代码应该是什么样子) @OfficerBacon 感谢您的回复,我确实有很多图像,因为背景由许多不同的瓷砖组成,我在每个瓷砖上单独绘制图像。这会导致闪烁吗? @AndrewThompson 谢谢你的帮助,下次我会努力整理和组织得更好。我已经注意到静态可以创建的问题,我会尽量避免它,但是很多时候我不能因为“你不能对非静态方法错误进行静态引用”我想我应该阅读主题并尝试改进 【参考方案1】:

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

动画可以通过 Swing Timer 完成,并带有周期性调用的方法。

这应该替换您的 while 循环。

这有点由内而外,但消除了处理所有问题的长循环。编写单独的事件处理程序,例如在按键按下时可以增加speed 计数器等。

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

【讨论】:

【参考方案2】:
g = Display.getCanvas().getGraphics();     

不要使用 getGraphics() 方法。

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

您还应该拥有: super.paintComponent(g);

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

【讨论】:

感谢您的帮助。我做了一些更改并尝试使用 super.paintComponent(g);而不是 getGaphics();但是我得到了一个 java.lang.NullPointerException。如果使用旧行: g = Display.getCanvas().getGraphics();问题没有发生。 @Aquan1111,在使用传递给 Swing 组件的 paintComponent() 方法的 Graphics 对象时,我从来没有遇到过 NPE,所以我不知道你在做什么。我建议您从 Custom Painting 上的 Swing 教程开始,先学习基础知识,然后再编写更复杂的绘画代码。 感谢您的回复,我已经发布了另一个问题,我认为我可以更好地解释问题,如果您也想查看它:link

以上是关于我在减少图像闪烁时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章

避免按下返回按钮时图像闪烁

Firefox 变换比例图像错误。使用悬停变换过渡时,图像会闪烁自身的一个小版本

我在 OpenGL 中显示图像时遇到问题

我在将图像添加到颤振项目时遇到问题

UIRefreshControl 闪烁

reactjs重新渲染时画布闪烁