您好我是一个没有经验的程序员,这是我关于堆栈溢出的第一个问题!

我正在尝试在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: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 Set中可见,使用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像素)而大大超过它。

    我的想法已经用完了,我的互联网搜索都无法让我进一步尝试以更好的方式做到这一点。我认为必须有一种有效地消除战争迷雾的方法,但是也许我对Java或可用工具还不够熟悉。

    非常感谢您提出关于是否可以改进当前尝试或是否应该尝试其他尝试的指示。

    谢谢!

    最佳答案

    这是一个很好的问题。我不熟悉awt / swing类型渲染,因此我只能尝试解释该问题的可能解决方案。

    从性能的角度来看,我认为在地图的较大部分中对FOW进行块化/栅格化是一个更好的选择,而不是使用基于像素的系统。这将减少每个刻度的检查量,并且更新它还将占用更少的资源,因为仅一小部分窗口/地图需要更新。网格越大,检查越少,但是视觉损失越大。

    那样做会使FOW看起来很块状/像素化,但这不是您无法解决的问题。

    对于玩家的直接周围环境,您可以添加一个以玩家为中心的圆形纹理。然后可以使用混合(我相信awt / swing中的术语是composite)来“覆盖”圆与FOW纹理重叠的alpha。这样,基于像素的更新由renderAPI完成,该API通常使用硬件增强方法来实现这些目的。 (对于自定义的基于像素的渲染,如果渲染API支持,通常会使用“着色器脚本”之类的东西)

    如果您仅需要在FOW中进行临时查看(如果您不需要“记住”地图),甚至不需要FOW的纹理网格,就足够了,但是我怀疑您确实想“记住” ' 地图。因此,在这种情况下:

    可以像使用基于网格的地形一样固定块状/像素化外观。基本上根据周围环境添加一些小的其他纹理/形状,以使外观看起来不错。下面的链接提供了很好的示例,以及有关如何进行“地形转换”的详细说明。

    https://www.gamedev.net/articles/programming/general-and-gameplay-programming/tilemap-based-game-techniques-handling-terrai-r934/

    我希望这能带来更好的结果。如果您无法获得更好的结果,我建议您切换到类似OpenGL之类的渲染引擎,因为这是用于游戏的,而awt / swing API主要用于UI /应用程序渲染。

    08-03 23:28