你好,我是一名经验不足的程序员,这是我在Stack Overflow上的第一个问题!
我正在尝试在我的Java游戏中实现“迷雾效果”。这意味着我的地图大部分开始都是黑色的,当我的角色周围移动时,地图的一部分将被揭示。我已经搜索了一些建议并尝试了调整它们自己。我的每个方法都有效,但是我遇到了很大的运行时问题。与此同时,在任何迷雾效果的尝试之前,我可以获得250-300 FPS。
这是我的基本方法:
- 渲染我的JPanel上的背景和所有对象
- 创建黑色BufferedImage(fogofwarBI)
- 确定需要可见的地图区域
- 将fogofwarBI上相关的像素设置为完全透明
- 渲染我的fogofwarBI,从而覆盖屏幕的一部分以黑色显示,并在透明部分允许查看背景和对象。
对于初始化缓冲图像,我在我的FogOfWar()类中完成了以下操作:
private BufferedImage blackBI = loader.loadImage("/map_black_2160x1620.png");
private BufferedImage fogofwarBI = new BufferedImage(blackBI.getWidth(), blackBI.getHeight(), BufferedImage.TYPE_INT_ARGB);
public FogOfWar() {
fogofwarBI.getGraphics().drawImage(blackBI,0,0,null);
}
在每次尝试中,我会将人物放在"可见"地形中部,也就是地图上没有雾的区域(我的雾图像有完全透明的像素的地方)。
尝试1:setRGB
首先,我找到了人物视野范围内的“新”坐标,如果它移动了的话。也就是说,并不是人物视野范围内的每个像素,而只是朝着移动方向的视野范围边缘的像素。这是通过一个for循环来完成的,最多会循环400个像素左右。
我将每个x和y坐标输入我的FogOfWar类。
我检查这些x、y坐标是否已经是可见的(如果是,我就不必再做什么工作以节省时间)。我通过维护一个Set of Lists来进行此检查。其中每个List包含两个元素:x值和y值。Set是一组独特的坐标列表。Set开始为空,我将添加x、y坐标来表示透明像素。我使用Set来保持集合的唯一性,因为我知道List.contains函数是进行此检查的快速方法。并且我将坐标存储在一个List中,以避免混淆x和y。
如果我的fogofwarBI上的给定x、y位置目前不可见,我使用.setRGB将其设置为透明,并将其添加到我的transparentPoints Set中,以便以后不会再次编辑那个坐标。
Set<List<Integer>> transparentPoints = new HashSet<List<Integer>>();
public void editFog(int x, int y) {
if (transparentPoints.contains(Arrays.asList(x,y)) == false){
fogofwarBI.setRGB(x,y,0); // 0 is transparent in ARGB
transparentPoints.add(Arrays.asList(x,y));
}
}
然后我使用下面的代码进行渲染:
public void render(Graphics g, Camera camera) {
g.drawImage(fogofwarBI, 0, 0, Game.v_WIDTH, Game.v_HEIGHT,
camera.getX()-Game.v_WIDTH/2, camera.getY()-Game.v_HEIGHT/2,
camera.getX()+Game.v_WIDTH/2, camera.getY()+Game.v_HEIGHT/2, null);
}
我基本上将我的fogofwarBI的正确部分应用到我的JPanel(800*600),具体取决于我的游戏相机所在位置。
结果: 正常工作。 在穿过雾时帧速率为20-30,否则正常(250-300)。 由于.setRGB函数被运行了多达400次每次我的游戏“ticks”,所以这种方法很慢。
尝试2:光栅
在这个尝试中,我创建一个我fogofwarBI的光栅,直接以数组格式处理像素。
private BufferedImage blackBI = loader.loadImage("/map_black_2160x1620.png");
private BufferedImage fogofwarBI = new BufferedImage(blackBI.getWidth(), blackBI.getHeight(), BufferedImage.TYPE_INT_ARGB);
WritableRaster raster = fogofwarBI.getRaster();
DataBufferInt dataBuffer = (DataBufferInt)raster.getDataBuffer();
int[] pixels = dataBuffer.getData();
public FogOfWar() {
fogofwarBI.getGraphics().drawImage(blackBI,0,0,null);
}
我的editFog方法现在看起来像这样:
public void editFog(int x, int y) {
if (transparentPoints.contains(Arrays.asList(x,y)) == false){
pixels[(x)+((y)*Game.m_WIDTH)] = 0; // 0 is transparent in ARGB
transparentPoints.add(Arrays.asList(x,y));
}
}
我的理解是光栅图与像素数组在(恒定?)通信,因此我以与尝试1相同的方式渲染BI。
结果: 正常工作。 大约15帧/秒的恒定FPS。 我认为它一直如此缓慢(无论我的角色是否穿过雾),因为虽然操作像素数组很快,但光栅图一直在工作。
尝试3:更小的光栅图
这是尝试2的变化。
我在某个地方读到,使用10个输入版本的.drawImage不断调整BufferedImage的大小会很慢。我也认为为2160*1620的BufferedImage设置光栅图可能会很慢。
因此,我尝试将“雾层”只设置为我的视图大小(800*600),并根据当前像素是否应该是黑色或从我的标准transparentPoints集合中可见以及基于我的相机位置,使用for循环更新每个像素。
现在,我的editFog类只更新不可见像素的集合,我的render类如下:
public void render(Graphics g, Camera camera) {
int xOffset = camera.getX() - Game.v_WIDTH/2;
int yOffset = camera.getY() - Game.v_HEIGHT/2;
for (int i = 0; i<Game.v_WIDTH; i++) {
for (int j = 0; j<Game.v_HEIGHT; j++) {
if ( transparentPoints.contains(Arrays.asList(i+xOffset,j+yOffset)) ) {
pixels[i+j*Game.v_WIDTH] = 0;
} else {
pixels[i+j*Game.v_WIDTH] = myBlackARGB;
}
}
}
g.drawImage(fogofwarBI, 0, 0, null);
}
因此,我不再动态调整我的fogofwarBI大小,而是每次更新每个像素。
结果: 正常工作。 帧速率:恒定1 FPS-最差的结果! 我猜不调整fogofwarBI大小并使其更小的任何节省都被在栅格中更新800 * 600像素而不是大约400个的操作所抵消了。
我已经没有想法,我的互联网搜索也没有让我更进一步了解如何以更好的方式实现这一点。我认为肯定有一种有效地实现战争迷雾的方法,但也许我还不够熟悉Java或可用工具。
非常感谢能否提供关于当前尝试是否可以改进或者是否应该尝试其他事情的任何指导。
谢谢!