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()
时,它会将组件重新绘制为其最初的空白外观——因此会发生闪烁。
消除闪烁很容易:覆盖JFrame
(GameMain
)的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 绘图窗口闪烁问题 )