你好,我是一个没有经验的程序员,这是我关于堆栈溢出的第一个问题!
我正在尝试在Java游戏中实现“战争迷雾”。这意味着我的大部分地图都从黑色开始,然后随着我的角色之一在地图各部分之间移动而被显示出来。我搜索了包括此处在内的内容,发现了一些建议,并尝试自己进行调整。我的每种方法都有效,但是每种方法都遇到了严重的运行时问题。为了进行比较,在我进行任何战争迷雾之前,我都获得了250-300 FPS。
这是我的基本方法:
为了初始化缓冲的图像,我在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:setRGB
首先,如果角色移动了,我会在其视野中找到“新”坐标。即。并非角色视野范围内的每个像素,而是角色视野范围内沿其移动方向的像素。这是通过for循环完成的,最多可以处理400个像素左右。
我将这些x和y坐标中的每一个都输入到FogOfWar类中。
我检查这些x,y坐标是否已经可见(在这种情况下,我不会为它们做任何事以节省时间)。我通过维护一组列表来进行此检查。其中每个列表包含两个元素:x和y值。集合是坐标列表的唯一集合。该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),并根据当前像素是黑色还是在我的标准transparentPoints中可见,使用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-效果最差! 我猜想,不更新我的fogofwarBI的大小而缩小它的任何节省都将通过更新栅格中的800 * 600像素而不是更新栅格中的400 * 600而大大超过了。
我的想法已经用光了,我的互联网搜索都无法使我进一步尝试以更好的方式做到这一点。我认为必须有一种有效地解决战争迷雾的方法,但是也许我对Java或可用工具还不够熟悉。
非常感谢您指出我目前的尝试是否可以改善,或者我是否应该尝试其他尝试。
谢谢!
答案 0 :(得分:1)
这是一个好问题。我对awt / swing类型的渲染并不熟悉,所以我只能尝试解释该问题的可能解决方案。
从性能的角度来看,我认为在地图的较大部分中对FOW进行块化/栅格化是一个更好的选择,而不是使用基于像素的系统。这将减少每个刻度的检查量,并且更新它还将占用更少的资源,因为仅一小部分窗口/地图需要更新。网格越大,检查越少,但是视觉上的损失越大。
保持这种状态会使FOW看起来很块状/像素化,但这不是您无法解决的。
对于玩家的直接周围环境,您可以添加一个以玩家为中心的圆形纹理。然后,您可以使用混合(我相信awt / swing中的术语是composite
)来“覆盖”圆与FOW纹理重叠的Alpha。这样,基于像素的更新由renderAPI完成,该API通常使用硬件增强方法来实现这些目的。 (对于自定义的基于像素的渲染,如果渲染API支持,通常会使用“着色器脚本”之类的东西)
如果您仅需要在FOW中进行临时观察(如果您不需要“记住”地图),甚至不需要FOW的纹理网格,就足够了,但是我怀疑您确实想要来“记住”地图。因此,在这种情况下:
可以像使用基于网格的地形一样固定块状/像素化外观。基本上根据周围环境添加一些小的纹理/形状,以使外观看起来不错。下面的链接提供了很好的示例,以及有关如何进行“地形转换”的详细说明。
我希望这会带来更好的结果。如果您无法获得更好的结果,我建议切换到OpenGL之类的渲染引擎,因为这是用于游戏的,而awt / swing API主要用于UI /应用程序渲染。