我遇到了一个非常奇怪的问题,我试着寻找几天和几天的答案。 我的游戏刚刚获得了一个新的粒子系统,但速度太慢而无法播放。不幸的是,BufferedImage转换非常慢。爆炸效果包括从.png文件加载的大约200个白色精灵,随机旋转,缩放和着色,以随机速度移动。
我试图通过三重/双重缓冲使性能更好,并遇到了一些问题。
我的第一次尝试是使用JPanel进行游戏。我在JFrame的类(Main)中设置缓冲区,然后在Game(扩展JPanel)类中完成绘图,但没有Graphics g = bufferstrategy.getDrawGraphics();.然后,在绘图方法结束时,我显示缓冲区IF没有丢失。缓冲区总是“丢失”,因为我没有使用它的Graphics对象进行绘图。但!游戏运行得和地狱一样快!在实际使用中没有缓冲!但是如何?
此尝试最终没有出现图形错误,并且性能提升很大 - 但仅限于nVidia / AMD显卡。英特尔GPU无法解决这个问题,屏幕闪烁白光。
所以,我最终设置并正确使用了BufferStrategy。 Game类现在扩展了Canvas,而不是JPanel,因为从JFrame获取Graphics,并且使用它在JPanel上绘制最终会产生偏移,因为它是在标题栏下绘制的。仍然很快,修复60 FPS。
现在,当我在JFrame(Main类)中创建BufferStrategy时,根本没有图片。我通过在Game类(Canvas)中设置BufferStrategy来纠正这个问题。现在图片是正确的,但游戏本身就像蜗牛一样慢。一次爆炸使FPS下降至~10,但仅限于nVidia / AMD。具有讽刺意味的。即使是老式的英特尔GPU也能以60 FPS处理它,我还是在一台具有5到6年历史的集成英特尔GPU上以60 FPS的速度运行了10000粒子。发生爆炸时,我的卡会碰到高达100%的负载。
这是我的主要代码(整个代码不清楚且很长):
public class Game extends Canvas {
-snip-
public void tick() {
BufferStrategy bf = getBufferStrategy();
Graphics g = null;
try {
g = bf.getDrawGraphics();
paint(g);
} finally {
g.dispose();
}
if (!bf.contentsLost()) {
bf.show();
} else {
System.err.println("Buffer lost!");
}
Toolkit.getDefaultToolkit().sync();
}
public void setBuffers() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
if (gc.getBufferCapabilities().isMultiBufferAvailable()) {
createBufferStrategy(3);
System.out.println("Triple buffering active");
} else {
createBufferStrategy(2);
System.err.println("Triple buffering not supported by the GPU");
System.out.println("Double buffering active");
}
System.out.println("FullScreen required: " + getBufferStrategy().getCapabilities().isFullScreenRequired());
System.out.println("Page flipping: " + getBufferStrategy().getCapabilities().isPageFlipping());
}
public void paint(Graphics g) {
super.paint(g);
//set up RenderingHints, draw stuff
}
-snip snip-
}
当然,我会在游戏开始后立即调用setBuffers()。
我希望你能帮助我,这个问题非常重要,因为在我看来使用VolatileImage不会带来性能提升,因为图像处理需要使用BufferedImage来完成。我敢打赌,我错过了一些微不足道的事情,或者做错了事。
以下是我的计算机规格,只是为了表明它不是硬件问题: 英特尔酷睿i7-3770k @ 4.3GHz, nVidia GTX 460, 12 GB ram
“快速”电脑: 英特尔酷睿2双核@ 2.7 GHz, 集成英特尔图形, 2 GB ram
感谢您的帮助和时间! :)
修改 VolatileImage有帮助吗?如果我知道的话,必须使用BufferedImages进行图像处理,但是绘制它们会很迟钝。
答案 0 :(得分:2)
以下是要检查的一些事项:
在不知道setBuffers函数的控制台/错误输出的情况下,很难说清楚。英特尔是否使用createBufferStrategy(2);而NV使用createBufferStrategy(3); ?如果是这样可能是使用额外内存的问题的一部分。
您是否尝试过java2d System.properties? http://docs.oracle.com/javase/1.5.0/docs/guide/2d/flags.html特别是用于调试的trace属性。
仅限Windows
System.setProperty("sun.java2d.transaccel", "True");
System.setProperty("sun.java2d.d3d", "True");
System.setProperty("sun.java2d.ddforcevram", "True");
所有平台
System.setProperty("sun.java2d.opengl", "True");
用于调试
System.setProperty("sun.java2d.trace", "timestamp,log,count");
//// -Dsun.java2d.trace=[log[,timestamp]],[count],[out:<filename>],[help],[verbose]
Toolkit.getDefaultToolkit()同步();实际上并没有强制监视器VSync,这是由BufferCapabilities(在你的setBuffers函数中)完成的。
BufferStrategy bf = getBufferStrategy();
if (bf != null) {
BufferCapabilities caps = bf.getCapabilities();
try {
Class ebcClass = Class.forName(
"sun.java2d.pipe.hw.ExtendedBufferCapabilities");
Class vstClass = Class.forName(
"sun.java2d.pipe.hw.ExtendedBufferCapabilities$VSyncType");
Constructor ebcConstructor = ebcClass.getConstructor(
new Class[] { BufferCapabilities.class, vstClass });
Object vSyncType = vstClass.getField("VSYNC_ON").get(null);
BufferCapabilities newCaps = (BufferCapabilities)ebcConstructor.newInstance(
new Object[] { caps, vSyncType });
createBufferStrategy(2, newCaps);
// TODO: if success, setCanChangeRefreshRate(false) and setRefreshRate(60).
// Possibly override refreshRateSync()?
}
catch (Throwable t) {
// Ignore
t.printStackTrace();
}
}
此外,您应该在Canvas构造函数运行后调用setBuffers并将其添加到JFrame中。
我“相对确定”你不需要打电话给super.paint(g);我不知道它是什么导致了你的具体问题,但你应该从你的绘画功能中删除该行。
虽然您没有显示代码,但使用200个随机移动的精灵的爆炸机制可能是导致错误的主要原因。计算出你想在屏幕上同时拥有的最大爆炸次数,并在手工生成这些精灵N * 200之后,将它们放入数组列表中的ArrayList(N),然后在你的代码中循环引用它们。 ExplosionClass = explosionList.get(currentExplosionIndex%explosionList.size());
答案 1 :(得分:1)
检查与缓存相比的图片大小,并尝试诊断缓存未命中数。阅读面向数据的设计,它通常是其中的一部分。
另一种方法是将爆炸的方式更改为不加载200个精灵并将其展开的方式(重复使用它们,还是每次爆炸时都加载它们?)。制作动画爆炸需要的功率较小,虽然它可能不那么壮观,但你需要做一些动画。
Java也不是制作游戏的最佳语言。你的工作水平非常高,而JVM的速度有点慢。我的朋友们在制作小游戏时遇到了类似的问题,很难制作一个不会落后的游戏。
答案 2 :(得分:0)
当您向GPU发送内容时,GPU会从GDRAM中读取信息。如果通过调用getDrawGraphics将数据读入内存,则内存可能会从图形卡读回RAM。 CPU只能访问RAM(DRAM),GPU只能访问GDRAM。对于板载GPU,这是不同的,因为它们没有自己的RAM,它们使用常规RAM的一部分。
要返回导致代码快速运行,确定您拥有的硬件,然后相应地调用适当的方法(代码更改之前或之后的方法)。