【发布时间】:2020-10-04 06:04:33
【问题描述】:
您好,我是一个没有经验的程序员,这是我关于 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);
}
在我的每次尝试中,我都会在“可见”地形的中间开始角色,即。在我的地图中没有雾的部分(我的 fogofwarBI 将具有完全透明的像素)。
尝试 1:设置RGB
首先,如果角色移动了,我会在角色的视野中找到“新”坐标。 IE。不是角色视线范围内的每个像素,而只是在他移动方向上他的视线范围边缘的像素。这是通过 for 循环完成的,最多会经过 400 个左右的像素。
我将这些 x 和 y 坐标中的每一个都输入到我的 FogOfWar 类中。
我检查这些 x,y 坐标是否已经可见(在这种情况下,我不会费心对它们做任何事情以节省时间)。我通过维护一组列表来进行此检查。其中每个 List 包含两个元素:x 和 y 值。 Set 是坐标列表的唯一集合。 Set 开始为空,我将添加 x,y 坐标来表示透明像素。我使用 Set 来保持集合的唯一性,因为我了解 List.contains 函数是执行此检查的快速方法。我将坐标存储在一个列表中以避免混淆 x 和 y。
如果我的 fogofwarBI 上的给定 x,y 位置当前不可见,我添加使用 .setRGB 将 RBG 设置为透明,并将其添加到我的 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)。
结果: 工作正常。 在雾中移动时 FPS 为 20-30,否则正常 (250-300)。 由于 .setRGB 函数,此方法很慢,每次我的游戏“滴答”时运行多达 400 次。
尝试 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。
结果: 工作正常。 恒定的 FPS 约为 15。 我相信它总是这么慢(不管我的角色是否在雾中移动),因为虽然操作像素阵列很快,但光栅一直在工作。
尝试 3:较小的栅格
这是尝试 2 的变体。
我在某处读到,使用 .drawImage 的 10 个输入版本不断调整 BufferedImage 的大小很慢。我还认为拥有 2160*1620 BufferedImage 的光栅可能会很慢。
因此,我尝试让我的“雾层”仅等于我的视图大小 (800*600),并使用 for 循环更新每个像素,具体取决于当前像素应该是黑色还是从我的标准透明点中可见设置并基于我的相机位置。
所以现在我的 editFog 类只更新了不可见像素集,我的渲染类看起来像这样:
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 的大小,而是每次都更新每个像素。
结果: 工作正常。 FPS:持续 1 FPS - 最糟糕的结果! 我猜想通过更新光栅中的 800*600 像素而不是大约 400 像素,可以大大超过不调整我的 fogofwarBI 大小并使其更小的任何节省。
我的想法已经用完了,我的互联网搜索都没有让我进一步尝试以更好的方式做到这一点。我认为必须有一种方法可以有效地制作战争迷雾,但也许我对 Java 或可用工具还不够熟悉。
非常感谢关于我目前的尝试是否可以改进或我是否应该完全尝试其他东西的指针。
谢谢!
【问题讨论】:
标签: java render raster bufferedimage frame-rate