Java 图形闪烁

Posted

技术标签:

【中文标题】Java 图形闪烁【英文标题】:Java graphics are flashing 【发布时间】:2013-01-09 06:42:41 【问题描述】:

好的,我了解您对 SSCCE 的需求,所以我创建了(我的第一个)一个。

我设法用不到 200 行代码复制了这个问题。在我的系统上,这个演示完美地编译并运行(当然只有闪烁仍然存在)。 我剥离了一切与它无关的东西。所以基本上我们现在有两个源文件:屏幕管理器和游戏管理器。

屏幕管理器: http://pastebin.com/WeKpxEXW

游戏管理员: http://pastebin.com/p3C5m8UN

您可以使用此 make 文件编译此代码(我使用 Linux 的移植版 make for Windows): 抄送=javac BASE = nl/jorikoolstra/jLevel CLASS_FILES = 类/$(BASE)/Game/GameMain.class 类/$(BASE)/Graphics/ScreenManager.class

jLevel: $(CLASS_FILES)
    @echo Done.

classes/%.class : src/%.java
    @echo Compiling src/$*.java to $@ [command: $(CC) src/$*.java ] ...
    @$(CC) -Xlint:unchecked -d classes -classpath src src/$*.java

源文件放在/src 目录中,类放在/classes 目录中。

编译成字节码后,可以使用以下 .bat 文件启动游戏:

@set STARUP_CLASS=nl.jorikoolstra.jLevel.Game.GameMain
@set ARGUMENTS=1280 1024 32
@java -cp classes;resources %STARUP_CLASS% %ARGUMENTS%

请注意,ARGUMENT 变量取决于您自己的屏幕设置,您必须更改它,以便游戏以适合您屏幕的分辨率显示。

【问题讨论】:

我还没有通读代码,但是您是否尝试过重新启动计算机?它经常为我解决闪烁问题。 是的,我做了,没有帮助。 退出全屏模式还有问题吗?您在 Linux 和 Windows 上使用相同的硬件加速吗?你可以试试这个:frame.getGraphicsConfiguration().getBufferCapabilities().isPageFlipping() 看看它的硬件是否加速。在排除它之前,请确保它在两个平台上都相同。 你有安装在 Windows 机器上的当前驱动程序吗? 第 18 章的原始代码(如上链接)在 Windows 上是否按预期运行? 【参考方案1】:

我明白它为什么闪烁了----

BufferStrategy 正在与组件的 paint() 方法进行单独的绘制工作,它们似乎使用不同的 Graphics 对象,并且它们以不同的速度刷新 --

paint()show() 之前调用时,没问题。但是

当在show() 之后调用paint() 时,它会将组件重新绘制为其最初的空白外观——因此会发生闪烁。


消除闪烁很容易:覆盖JFrameGameMain)的paint()方法,因为你不需要它来做任何事情(BufferStrategy可以让你更精确地控制绘画的东西) :

@Override
public void paint (Graphics g) 

就是这样。 (我已经测试过了,它工作正常,希望这可能会有所帮助:))


===== 更新=====

而不是覆盖paint() 方法,更好的方法是为您的JFrame (GameMain) 调用setIgnoreRepaint(true)——这个方法就是为此目的而设计的! 使用它

private GameMain(String ... args)

    setIgnoreRepaint(true);
    .....

【讨论】:

@Jori 我想会的。它是Java 的绘画方案,与平台无关。你试过了吗? 对。这正是我发布的代码链接所显示的。你调用的是 bufferStrategy.show(),而不是其他的。 是的,它完成了工作:)。谢谢。 显然这是依赖于平台的,为什么在没有setIgnoreRepaint(true); 行的情况下它可以在Linux 上运行? Ubuntu 运行 Gnome 桌面是的,安装了默认的 JDK。【参考方案2】:

这就是我实现双缓冲的方式,可能会帮助您了解这个概念。 请注意它是在 JPanel 中实现的,但我认为它可以在其他容器中实现:

TheJApplet.java:

import java.awt.*;
import javax.swing.*;

public class TheJApplet extends JApplet

    private Image myImage;

    java.net.URL GameURL = CheckerGameJApplet.class.getResource("GameIMG");

    String GamePath = GameURL.getPath();

    @Override
    public void init()
    
        String Graphpath = GamePath+"/";

        File myImage_File = new File(GraphPath+"myImage.jpg");

        try
        
            myImage = ImageIO.read(myImage_File);
        
        catch (IOException ex)
        
            // Add how you like to catch the IOExeption
        

        final TheJPanel myJPanel = new TheJPanel(myImage);

        add(myJPanel);
    

JPanel.java:

import java.awt.*;
import javax.swing.*;

public class TheJPanel extends JPanel

    private int screenWidth  = 500;
    private int screenHeight = 500;

    private BufferedImage BuffImg = new BufferedImage
                                         (screenWidth, 
                                          screenHeight,
                                          BufferedImage.TYPE_INT_RGB);

    private Graphics2D Graph = BuffImg.createGraphics();

    private Image myImage;

    public TheJPanel(Image myImage)
    
        this.myImage = myImage;

        repaint();
    

    @Override
    public void paintComponent(Graphics G)
    
        Graphics2D Graph2D = (Graphics2D)G;

        super.paintComponent(Graph2D);

        if(BuffImg == null)
        
            System.err.println("BuffImg is null");
        

        Graph.drawImage(myImage, 0, 0, this);

        Graph2D.drawImage(BuffImg, 0, 0, this);
    

希望这会有所帮助,祝你好运。

【讨论】:

在您的 JPanel 中调用 super.paintComponent() 可能会触发背景的重绘 - 我会跳过该行。此外,通过 setDoubleBuffered(true) 在 JComponent 中原生支持双缓冲 我以这种方式制作了一款棋盘游戏并且运行良好,您所说的“可能会触发重新绘制背景”是什么意思,对我来说没有多大意义,您能进一步解释一下吗?我会看一下 setDoubleBuffered(true) 并检查它是否会减少我的代码中的行数,虽然我记得我曾在我的棋盘游戏上尝试过但没有用,仔细检查没有害处,感谢您的提示和评论。 当您调用 super 方法时,它会绘制默认的组件外观,如果您自己绘制组件的全部内容,则不需要。这也可能导致闪烁。【参考方案3】:

我有一个基于 Java AWT 的跨平台动画程序。在我严格遵循in the Java BufferStrategy documentation 的示例代码之前,它一直存在闪烁问题。但是,我使用的是嵌入在 Swing 层次结构中的 AWT Canvas,而不是像您一样全屏显示。有兴趣的可以see the code here。

另外需要注意的是,AWT 管道使用 OpenGL 原语来获得良好的性能,而 OpenGL 支持在许多视频驱动程序中存在缺陷。尝试为您的平台安装最新版本的驱动程序。

【讨论】:

【参考方案4】:

当您在自己的方法中设置 hwnd.createBufferStrategy(2) 时,它可能对您有用。

【讨论】:

你的意思是initializeGameEnvironment()方法? 应该可以,但最好是这样:public void createStrategy() createBufferStrategy(2); strategy = getBufferStrategy(); 并在您的第一个屏幕可见后调用它 不幸的是它不起作用:(,我在screenManager.setFullScreen(displayMode, this);之后粘贴了createBufferStrategy(2),它仍然快速闪烁。我也不知道为什么这会起作用,毕竟hwnd是对在该引用上调用的 JFrame 和方法与从内部类调用它完全相同,对吗?无论如何感谢您的帮助,也许您可​​以找到真正的错误。【参考方案5】:

Java 呈现透明背景GIF 图像时出现问题。这可能是问题所在。

【讨论】:

我只在精灵的渲染循环中使用 .png 图像。 另外,在新示例中,根本没有使用任何图像,只有文本,而且它仍然在闪烁。【参考方案6】:

如果没有 SCCSE,我发现很难回答您的问题。我也想知道 RepaintManagerResetter 是做什么的。

您可能希望将背景颜色设置为一些花哨的颜色,例如 0xFF00FF,以了解是否有人在绘图发生之前“清除”了背景。如果闪烁的图像是紫色的,那就是 - 如果它包含垃圾或旧图像,则可能是双缓冲。

无论如何,我会尽量确保没有人会期待你自己。首先,尽量防止原生 Windows 代码绘制窗口背景。设置一次:

/*
 * Set a Windows specific AWT property that prevents heavyweight components 
 * from erasing their background. 
 */
System.setProperty("sun.awt.noerasebackground", "true");

另外,请确保在 JFrame 中覆盖它(如果您使用的是 Swing 组件)

@Override
public void paintComponent(Graphics G)

  // do not call super.pC() here
  ...

如果这没有帮助,请提供您的代码的工作示例,以便人们可以重现问题。

【讨论】:

System.setProperty("sun.awt.noerasebackground", "true");没有完成这项工作,在新示例中我也没有使用任何 JComponents。

以上是关于Java 图形闪烁的主要内容,如果未能解决你的问题,请参考以下文章

Java AWT 图形界面编程Canvas 组件中使用 Graphics 绘图 ④ ( AWT 绘图窗口闪烁问题 )

在 Delphi 中进行非闪烁、分段图形更新的最佳方法?

C# 滚动自定义图形以减少闪烁的更好方法

在第一代 3.1.3 上拖动 uiScrollview 时,uiSlider 拇指图形“闪烁”

echarts饼图鼠标移到图形上出现闪烁 无法点击问题

在 MFC CPaintDC 中进行双缓冲后闪烁不会减少