对于一个类项目,我和其他一些人正在开发一个等距游戏。所有内容都使用缓冲图像在一个 JPanel 中绘制。每件艺术品都是用 Fireworks 完成的,并保存为 .png。
在 Linux 上,每个游戏周期重绘 map 所需的时间约为 3 毫秒。在 Windows(以及 OSx)上,大约为 100 毫秒,峰值为 500 毫秒。
在 4 种不同的计算机(从典型笔记本电脑到 i7-3770K + 660 游戏机)上观察到了这种效果。发生这种情况时,CPU 使用率约为 10-20%,程序的 RAM 使用率约为 1GB。这个问题在网上查了很多地方都没有结果,连我们负责项目的科长也很困惑。有什么想法吗?
这里是JPanel中的paint组件和setEntityImage方法。您可以在paintComponent 的底部看到时间戳的提取位置。 setEntityImage 每 500 毫秒设置一次新的 BufferedImage,该新的 BufferedImage 在传递到此 JPanel 之前在另一个线程上预先绘制。我包含该代码是因为我也很好奇这是否可能是线程问题。
此代码每 500 毫秒调用一次,包含一个“游戏刻度”,它绘制图像(全部预加载在静态单例类中)。
BufferedImage entityImg = ImageUtil.getFromGraphics(map.getWidth() * TILE_WIDTH
+ TILE_WIDTH, map.getHeight() * TILE_HEIGHT + TILE_HEIGHT);
Graphics2D g2 = (Graphics2D) entityImg.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (int i = 0; i < map.getHeight(); i++)
{
for (int j = 0; j < map.getWidth(); j++)
{
try
{
int x, y;
x = (j * TILE_WIDTH_HALF - i * TILE_WIDTH_HALF)
+ map.getWidth() * TILE_WIDTH_HALF;
y = j * TILE_HEIGHT_HALF + i * TILE_HEIGHT_HALF;
Entity e = map.getTile(i, j).getEntity();
if (e != null)
{
g2.drawImage(e.getImage(), x, y, 32, 32, null);
}
}
catch (Exception e)
{
}
}
}
panel.setEntityImage(entityImg);
此代码位于我们的 MapPanel 中,它扩展了 JPanel。
public void setEntityImage(BufferedImage entityImg)
{
this.entityImg = entityImg;
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics g2 = (Graphics2D) g;
g2.fillRect(0, 0, tileImg.getWidth(), tileImg.getHeight());
// Draw tiles
g2.drawImage(tileImg, 0, 0, null);
// Draw box over square mouse is hovering over
if (hover)
{
g2.setColor(new Color(255, 255, 255, 128));
int screenX = (this.x * 16 - this.y * 16) + Window.WIDTH * 16;
int screenY = (this.x * 8 + this.y * 8) + 16;
g2.drawLine(screenX, screenY + 8, screenX + 16, screenY);
g2.drawLine(screenX + 16, screenY, screenX + 32, screenY + 8);
g2.drawLine(screenX, screenY + 8, screenX + 16, screenY + 16);
g2.drawLine(screenX + 16, screenY + 16, screenX + 32, screenY + 8);
}
long time = System.currentTimeMillis();
g2.drawImage(entityImg, 0, 0, null);
System.out.println(System.currentTimeMillis() - time);
}
最佳答案
正在进行某种内部优化缓存,具体细节我不知道。本质上,如果 BufferedImage
未更新,它将在 0 毫秒内绘制。
我做了两次优化,不确定是否单独工作,但它对性能产生了影响,但对时间延迟没有影响(即,当我拖动 map 时,它明显更好)
第一个是在Window#tickUpdate
中。相反,每 500 毫秒创建一个新的 BufferedImage,这只是重复使用单个 BufferedImage,仅在没有先前实例或大小已更改的情况下创建新实例...
public void tickUpdate(Map map) {
int width = map.getWidth() * TILE_WIDTH + TILE_WIDTH;
int height = map.getHeight() * TILE_HEIGHT + TILE_HEIGHT;
if (entityImg == null || entityImg.getWidth() != width || entityImg.getHeight() != height) {
entityImg = ImageUtil.getFromGraphics(width, height);
}
Graphics2D g2 = (Graphics2D) entityImg.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR));
g2.setBackground(new Color(255, 255, 255, 0));
g2.clearRect(0, 0, width, height);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
for (int i = 0; i < map.getHeight(); i++) {
for (int j = 0; j < map.getWidth(); j++) {
Entity e = map.getTile(i, j).getEntity();
if (e != null) {
int x, y;
x = (j * TILE_WIDTH_HALF - i * TILE_WIDTH_HALF)
+ map.getWidth() * TILE_WIDTH_HALF;
y = j * TILE_HEIGHT_HALF + i * TILE_HEIGHT_HALF;
g2.drawImage(e.getImage(), x, y, 32, 32, null);
}
}
}
g2.dispose();
panel.setEntityImage(entityImg);
panel.repaint();
}
由于 BufferedImage
可以在绘制时进行更新,因此在 MapPanel
中,我创建了另一个 BufferedImage
,在其上绘制了“实体”图像。
public void setEntityImage(BufferedImage img) {
entityLock.lock();
try {
if (entityImg == null || entityImg.getWidth() != img.getWidth() || entityImg.getHeight() != img.getHeight()) {
entityImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
}
Graphics2D g2d = entityImg.createGraphics();
g2d.drawImage(img, 0, 0, null);
g2d.dispose();
} finally {
entityLock.unlock();
}
}
entitytLock
只是一个 ReentrantLock
,我用它来阻止 paintComponent
方法在更新时尝试绘制 BufferedImage
。
这不会改变时间,但在视觉上变得更好......
另一种方法是使用 VolatileImage
...
关于Java 的 g.drawImage 在 Windows 上比 Linux 上花费的时间明显更长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23508187/