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