当前位置: 首页 > 知识库问答 >
问题:

如何加快JLabels巨大平铺网格的加载时间

赫连冠玉
2023-03-14

我正在尝试创建一个天空视角的RPG游戏,但是初始加载时间已经成为一个问题。我对编程和Java相当陌生,所以我不太了解Java如何将东西加载到内存或任何东西中。所以问题是,当我最初运行我的程序时,我的所有JLabels(在我的网格中设置)可能需要很长时间才能出现。我希望制作一个非常大的地图,总共由数十万个图块组成。经过一些数学和html" target="_blank">测试,事实证明30秒的加载只会产生大约6724个图块。这只是8个屏幕多一点的地图,非常小,因为我的角色比50x50像素小一点。所以对于一个我可能满意的大小的地图,每次运行都需要6分钟以上的时间来加载。这太疯狂了。我的地图创建者加载得更慢。

每个图块为50x50像素。每个图块都设置为具有多层JLabels。例如,第一个是仅用于将其他标签添加到的JLabel,然后第二个是用于地形(如Grass),第三个是用于交互式元素,例如门,以便门可能出现在草的顶部。

我的所有平铺对象都将创建并插入到二维平铺阵列中。每个磁贴实例都有:

2个标签。

图标至少添加到第一个JLabel,通常仅添加到第一个JLabel。每次磁贴请求图标时,它都会从MapTiles类获取它。

5个字符串数组

7个布尔人

4整数

该大小的图层阵列中有4个图层实例

每个Tile实例还实现了MouseListener

每个图层实例具有:

1个无图标的标签,除非是草地。(75%的图层实例不是草)

4布尔值

1 int

1个字符串

MapTiles类将为该事物创建一个ImageIcon,例如“Grass”,如果它还没有。如果它已经为它创建了一个ImageIcon,那么它只是返回该ImageIcon。从阅读有关改进加载时间的文章中,我被引导这可能有助于加快速度,而不是为每个Tile创建一个新的ImageIcon。

在这里,我将为我的3个主要类中的每一个提供创建/加载网格的开始。我所说的开始,只是指构造函数和对象的创建。每个类中还有一些其他方法,但不是很多,并且在初始加载期间没有使用。

瓷砖类:

package Tiles;
import Datas.*;
import Images.*;
import MapCreation.*;
import UniversalVariables.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
public class Tile implements MouseListener{
    public Layer[] layers = new Layer[4];
    //MAKE SURE THAT THE FOLLOWING INTEGERS MATCH THE ARRAY IN MAPDATA
    int terrain = 0;
    int interactive = 1;
    int destroyable = 2;
    int obstruction = 3;

    public JLabel image = new JLabel();
    JLabel mouseDetector = new JLabel();
    boolean hasBarrier = false;
    boolean hasModifiedBarrier = false;
    boolean isInteractive = false;

    boolean leftClickPressed = false;
    boolean rightClickPressed = false;
    boolean scrollClickPressed = false;
    String[] layerTypes = MapData.uni.tileLayerTypes;
    String[] terrainTypes = MapData.uni.terrainTypes;
    String[] interactiveTypes = MapData.uni.interactiveTypes;
    String[] destroyableTypes = MapData.uni.destroyableTypes;
    String[] obstructionTypes = MapData.uni.obstructionTypes;
    int tileSize = MapData.uni.tilePixelSize;
    int row = 0;
    int col = 0;
    boolean mapCreatorOpen = UVars.uni.mapCreatorRunning;

    public Tile(int rowNum, int colNum){
        row = rowNum;
        col = colNum;
    setLayer("Terrain", "Grass");
    for(int c = 1; c < layers.length; c++){ //Sets all layers except terrain to have new layerName
        layers[c].layerName = MapData.uni.tileLayerTypes[c] + "NLT";
    }
    mouseDetector.addMouseListener(this);
    image.add(mouseDetector);
    mouseDetector.setBounds(0, 0, tileSize, tileSize);
    //image.setComponentZOrder(mouseDetector, 0);
    for(int c = 0; c < layers.length; c++){
        image.setComponentZOrder(layers[c].image, c);
    }
    image.setComponentZOrder(mouseDetector, 0);
}

}

层类:

package Tiles;
import Datas.*;
import Images.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class Layer{
    public JLabel image = new JLabel();
    int tileSize;
    public String layerName = "No Layer Name";
    boolean hasBarrier;
    boolean hasModifiedBarrier;
    boolean isInteractive;
    boolean affectsMovement;

    public Layer(Tile tile, String layerType){
        //For empty layers
        tile.add(image);
    }
    public Layer(Tile tile, String layerType, String name){
        if(layerType.equals("Terrain") && name.equals("No Layer Name")){
            name = "Grass";
        }
        layerName = name;
        tileSize = tile.tileSize;
        tile.add(image);
        image.setBounds(0, 0, tileSize, tileSize);
        image.setIcon(MapTiles.uni.getIcon(layerName));
        if(layerType.equals("Terrain")){
            Terrain terrain = new Terrain(layerName);
            terrain.exchangeValues(this, layerName);
        } else if(layerType.equals("Interactive")){
            Interactive interactive = new Interactive(layerName);
            interactive.exchangeValues(this, layerName);
        } else if(layerType.equals("Destroyable")){
            Destroyable destroyable = new Destroyable(layerName);
            destroyable.exchangeValues(this, layerName);
        } else if(layerType.equals("Obstruction")){
            Obstruction obstruction = new Obstruction(layerName);
            obstruction.exchangeValues(this, layerName);
        }
    }
}

MapTiles类:

package Images;
import Datas.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
public class MapTiles{
    public static MapTiles uni;
    String tileLoc = "Tile Graphics\\" + String.valueOf(MapData.uni.tilePixelSize) + "PX\\";
    ArrayList<ImageIcon> icons = new ArrayList<ImageIcon>();
    ArrayList<String> iconList = new ArrayList<String>();
    public MapTiles(){
        uni = this;
    }
    public ImageIcon getIcon(String tileType){
        for(int c = 0; c < iconList.size(); c++){
            if(iconList.get(c).equals(tileType)){
                return icons.get(c);
            }
        }
        iconList.add(tileType);
        ImageIcon newIcon = new ImageIcon(tileLoc + tileType + ".png");
        icons.add(newIcon);
        return newIcon;
    }
}

那么,为什么我的网格需要这么长时间才能加载呢?它都是在默认的Java线程中创建的,但那是因为目前,在进入地图之前,我没有任何菜单或其他任何东西来分散用户的注意力。地图立即开始。出现的只是一个空白面板。最终,一旦它最终在左上角创建了磁贴,一旦它走得那么远(用户视图的默认值),您就可以看到它创建了每个磁贴,用户甚至可以悬停在其中一个上。(悬停在它上面会改变上面设置的图标)悬停在它上面运行得很好。只有加载时间是问题。首先,对于创建这样一个网格,你们有什么更好的想法吗?其次,你能想到更好的方法来做我正在尝试做的事情吗?最后,我做错了什么需要改进的吗?我想我要问的主要问题是...是什么让Java需要这么长时间来加载新的Tile实例?顺便说一句,我有一台相当高端的PC。它不是垃圾。例如,我可以在最大图形设置下运行大多数2015年之前的电脑游戏,并且我有16GB的内存。我还有另一个问题你们可以回答。我读到使用图形处理器加载东西而不是主要将它们加载到内存中运行得更快,但它使用3DJava或类似的东西。我能使用我的图形处理器以某种方式加载许多瓷砖并以这种方式提高加载时间吗?

共有2个答案

苏鸿波
2023-03-14

拥有一张无尽的地图并不难实现,主要是很难维护

诀窍是您不会一次加载所有磁贴,而只加载可见的磁贴,因此称为视口。

public class ViewPort{
    private Tile[][] visibleTiles;

    public ViewPort(int width, int height, Factory factory){
        //create the Tiles and add them using a proper layout
        ....
    }

    public void setViewLocation(int x, int y){
    for(int dy = 0; dy < getHeight(); dy++){ //getHeight() with height from constructor
        for(int dx = 0; dx < getWidth(); dy++){ //getWidth() with width from constructor 
            visibleTiles[dx][dy] = factory.getTiles(dx+x, dy+y);
        } 
    }
}

如果你使用这种设计,你可以跳转到地图上的任何位置,你只需要使用一个合适的工厂。

更难的问题是创建一个好工厂:

public class Factory{
    public Tile getTile(int x, int y){
        if (x==0 && y==0) return getTile(TileType.Grass);
        ...
        return null;    
    }

    //speeding up using a lookup table, so we only create objects 
    //when we need new one / reUse old ones
    private Map<TileType, Tile> lookUpTable = ...;
    private Tile getTile(TileType type){
        Tile value = lookUpTable.get(type);
        if(value == null){
            value = new Tile(...);
            lookUpTable.put(type, value);
        }
        return value;
}

现在看起来简单的东西很难维护:

if (x==0 && y==0) return getTile(TileType.Grass);
if (x==1 && y==0) return getTile(TileType.Grass);

这段代码既不可维护也不高效,您应该使用开关/case

switch(x){
    case 0: 
    switch (y){
        case 0: return getTile(TileType.Grass);
        ...
    }
}

既不可维护也不可读。。。

姬旭
2023-03-14

嗯,看起来您正在从磁盘多次加载相同的图像以进行平铺。只有加载您需要的内容,然后在内存中共享相同的资源才能有所帮助。

此外,您可能不应该尝试将整个地图保存在内存中,而是根据需要加载每个部分。

我建议您放弃Swing,使用LWJGL或LWJGL支持的引擎。这将为您提供通常在Swing中不可用的硬件加速渲染(除非传递某些运行时标志)如果您担心质量,Minecraft将使用LWJGL。如果您打算使用Java进行游戏开发,那么这确实是一条路要走。

我特别推荐LIBGDX作为您的LWJGL支持的游戏框架。我已经广泛使用了它,这是一种在多个操作系统和移动设备上运行游戏的好方法。

 类似资料:
  • 问题内容: 例如,我有一些.article类,并且我想将该类作为网格视图进行查看。所以我采用了这种风格: 这种样式将使.article看起来平铺/网格化。固定高度可以正常工作。但是,如果我要将高度设置为自动(根据其中的数据自动拉伸),则网格看起来很讨厌。 问题答案: 这种布局称为砌体布局。石工是另一种网格布局,但它将填充由元素高度不同引起的空白。 jQuery Masonry是创建砌体布局的jQu

  • 问题内容: 我对此有疑问。我有一个JPanel,通常我会像这样创建一个JLabel: 但是我希望每次单击一个按钮时,在该面板中创建一个新的JLabel,它的大小相同,但高度不同。我试过了: 但是这样一来,我就无法设定界限。我从JTextField获得的stringName。 问题答案: 首先,使用layout。正确完成布局后,组件将按照需要放置。其次,在向布局动态添加组件时,您需要告诉布局更新。这

  • 在运行测试时,我需要长时间的延迟(约40秒)。 我看到了,在那段时间里,Selenium会话被删除了。 请帮助:如何配置会话超时以增加? 在Selenium节点日志中开始延迟后的30秒内,我看到了以下内容: 信息org.openqa.selenium.remote.server。DriverServlet-由于客户端超时,会话7f5fffec-4882-4c4c-b091-c780c66d379d

  • 问题内容: 我正在构建一个小的Flask应用程序,该应用程序在后台使用卷积神经网络对用户上传的图像进行预测。如果我这样加载它,它将起作用: 但是,这要求在用户添加图像之后加载分类器(clf)。这需要一段时间,因为它需要根据一个pickle文件为200层以上的神经网络设置所有权重。 我想要做的是在生成应用程序时加载所有权重。为此,我尝试了此操作(为HTML模板/导入/应用启动切出了不相关的代码):

  • 是否有可能强制Vaadin8网格从后端热切地加载所有行?目前,它只显示屏幕上可见的记录,并在网格滚动时获取下一行。

  • 问题内容: 二者并具有可用于当网页被完全加载到检测信号。问题是网页上有一些异步加载的内容(ajax)。在这种情况下如何知道页面何时完全加载? 问题答案: 我实际上尚未做到这一点,但我认为您 可以 使用来实现您的解决方案。 您可以使用networkAccessManager ()函数从QWebPage获取QNetworkAccessManager 。QNetworkAccessManager的信号完