如何加快JLabels

时间:2016-06-07 11:16:25

标签: java instance loading jlabel imageicon

我正在尝试创建一个天空视图RPG游戏,但初始加载时间已成为一个问题。我是编程和Java的新手,所以我不太了解Java如何将内容加载到内存或任何东西。 所以问题在于,当我最初运行我的程序时,可能需要很长时间才能显示我的所有JLabel(在我的网格中设置)。我希望制作一张非常大的地图,总共包含数十万个瓷砖。经过一些数学和测试后,结果表明30秒的加载只会产生大约6,724个瓷砖。由于我的角色比50x50像素小一点,因此这只是一张超过8屏幕的地图,这是非常小的。因此,对于我可能满意的大小的地图,每次运行都需要花费6分钟来加载。那太疯狂了。我的地图创建者加载速度更慢。

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

创建所有Tile对象并将其插入到二维Tile数组中。每个Tile实例都有:

2 JLabels。

图标至少添加到第一个JLabel,通常只添加到第一个JLabel。每当一个tile请求一个图标时,它就会从MapTiles类中获取它。

5个字符串数组

7 poleans

4 ints

此大小的图层数组中的4个图层实例

每个Tile实例也实现了MouseListener

每个Layer实例都有:

1 JLabel没有图标,除非是Grass。 (75%的Layer实例不是Grass)

4布尔

1 int

1 String

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年之前的PC游戏,并且我有16GB的RAM。我还有另外一个问题,你们可以回答。我读到使用GPU加载东西而不是主要将它们加载到RAM中运行得更快,但它使用3D Java或类似的东西。我是否可以使用我的GPU以某种方式加载许多磁贴并以这种方式改善加载时间?

2 个答案:

答案 0 :(得分:1)

好吧,看起来您正在从磁盘上多次为图块加载相同的图像。只加载你需要的东西,然后在内存中共享相同的资源可能有所帮助。

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

我建议你放弃Swing并使用LWJGL或LWJGL支持的引擎。这将为您提供通常不能通过Swing获得的硬件加速渲染(除非传递某些运行时标志。)如果您关注质量,Minecraft使用LWJGL。如果您打算使用Java进行游戏开发,那么它真的是一条路。

具体来说,我推荐LIBGDX作为LWJGL支持的游戏框架。我已经广泛使用它,它是在多个OS 移动设备上运行游戏的好方法。

答案 1 :(得分:0)

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

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

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);

这段代码既不可维护也不高效,你应该使用switch / case

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

既不可维护也不可读......