对于一个课程项目,我和其他一些人正在研究等距游戏。使用缓冲图像在一个JPanel中绘制所有内容。每件艺术品都在烟花中完成并保存为.png。
在Linux上,每个游戏刻度重绘地图所需的时间约为3毫秒。在Windows(以及OSx)上,它大约需要100ms才能达到500ms。
在4台不同的计算机上观察到此效果,从典型的笔记本电脑到i7-3770K + 660游戏机。发生这种情况时的CPU使用率约为10-20%,程序的RAM使用率约为1GB。这个问题已在很多地方在互联网上进行了研究,但是我们的部门负责人(负责该项目的人)也很难过。有什么想法吗?
这是JPanel中的paint组件和setEntityImage方法。您可以在paintComponent的底部看到时间戳的位置。 setEntityImage每500ms设置一个新的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);
}
答案 0 :(得分:0)
有一些内部优化缓存,我不知道的细节。基本上,如果BufferedImage
没有更新,它会在0毫秒内绘制。
我做了两次优化,不确定是否单独工作但是它在性能上有所不同,但不是在时间延迟(即,当我拖动地图时,它明显更好)
第一次是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
...