Java 地图分块策略,重块滞后问题

Java 地图分块策略,重块滞后问题,java,memory-management,Java,Memory Management,我很难想出一个好问题的标题。。。抱歉/如果你的大脑比我的少,请编辑 我在处理游戏地图客户端时遇到一些问题。我的游戏是基于32x32像素瓷砖的。我的第一张游戏地图是1750 x 1750瓷砖。我在客户端有很多层,但设法把它减少到2层(地面和建筑物)。我之前将整个地图的图层加载到内存中(短数组)。当我跳到2200x2200块时,我注意到一台旧电脑内存不足(1GB+)。我希望在byte和short之间有一个数据类型(我的目标是~1000个不同的tile)。我的游戏支持多种分辨率,因此玩家的可见空间可以

我很难想出一个好问题的标题。。。抱歉/如果你的大脑比我的少,请编辑

我在处理游戏地图客户端时遇到一些问题。我的游戏是基于32x32像素瓷砖的。我的第一张游戏地图是1750 x 1750瓷砖。我在客户端有很多层,但设法把它减少到2层(地面和建筑物)。我之前将整个地图的图层加载到内存中(短数组)。当我跳到2200x2200块时,我注意到一台旧电脑内存不足(1GB+)。我希望在byte和short之间有一个数据类型(我的目标是~1000个不同的tile)。我的游戏支持多种分辨率,因此玩家的可见空间可以显示23,17个瓷砖,分辨率为800x600,最高可达45,29个瓷砖,分辨率为1440x1024(加上)。我使用“平铺”绘制地图,并使用类似于以下格式的格式(0、0、2、0、3、6、0、74、2…)将两个图层输出到单独的文本文件中,所有这些都在一行上

在许多问题和研究的帮助下,我想出了一个地图组块策略。使用玩家的当前坐标作为中心点,我加载了足够5倍于可视地图大小的瓷砖(最大值为45*5,29*5=225145瓷砖)。玩家总是被画在中间,地面在他/她下方移动(当你向东走时,地面向西移动)。绘制的小地图在各个方向上显示一个屏幕,大小是可见地图的三倍。请看下面(非常缩小)的视觉表示,以更好地解释我可能解释了它

我的问题是:当玩家从原始中心点(chunkX/Y)坐标移动“块大小的1/5块”时,我要求游戏重新扫描文件。新的扫描将使用玩家的当前坐标作为其中心点。目前我遇到的问题是,在我的电脑上进行重调需要.5秒(这是相当高的规格)。贴图不会更新1-2次平铺移动

为了解决上述问题,我尝试在一个新线程中(在到达1/5点之前)将文件扫描到一个临时的arraybuffer中。扫描完成后,我会将缓冲区复制到实际数组中,并调用repaint()。随机地,我看到了一些与此无关的跳过问题。更糟糕的是,我看到它在地图上随机画了1-2帧。代码示例如下:

private void checkIfWithinAndPossiblyReloadChunkMap(){
    if (Math.abs(MyClient.characterX - MyClient.chunkX) + 10 > (MyClient.chunkWidth / 5)){ //arbitrary number away (10)
        Runnable myRunnable = new Runnable(){
            public void run(){
                logger.info("FillMapChunkBuffer started.");

                short chunkXBuffer = MyClient.characterX;
                short chunkYBuffer = MyClient.characterY;

                int topLeftChunkIndex = MyClient.characterX - (MyClient.chunkWidth / 2) + ((MyClient.characterY - (MyClient.chunkHeight / 2)) * MyClient.mapWidth); //get top left coordinate of chunk
                int topRightChunkIndex = topLeftChunkIndex + MyClient.chunkWidth - 1; //top right coordinate of chunk

                int[] leftChunkSides = new int[MyClient.chunkHeight];
                int[] rightChunkSides = new int[MyClient.chunkHeight];

                for (int i = 0; i < MyClient.chunkHeight; i++){ //figure out the left and right index points for the chunk
                    leftChunkSides[i] = topLeftChunkIndex + (MyClient.mapWidth * i);
                    rightChunkSides[i] = topRightChunkIndex + (MyClient.mapWidth * i);
                }

                MyClient.groundLayerBuffer = MyClient.FillGroundBuffer(leftChunkSides, rightChunkSides);
                MyClient.buildingLayerBuffer = MyClient.FillBuildingBuffer(leftChunkSides, rightChunkSides);

                MyClient.groundLayer = MyClient.groundLayerBuffer;
                MyClient.buildingLayer = MyClient.buildingLayerBuffer;
                MyClient.chunkX = chunkXBuffer;
                MyClient.chunkY = chunkYBuffer;

                MyClient.gamePanel.repaint();

                logger.info("FillMapChunkBuffer done.");
            }
        };
        Thread thread = new Thread(myRunnable);
        thread.start();
    } else if (Math.abs(MyClient.characterY - MyClient.chunkY) + 10 > (MyClient.chunkHeight / 5)){ //arbitrary number away (10)
        //same code as above for Y
    }
}

public static short[] FillGroundBuffer(int[] leftChunkSides, int[] rightChunkSides){
    try {
        return scanMapFile("res/images/tiles/MyFirstMap-ground-p.json", leftChunkSides, rightChunkSides);
    } catch (FileNotFoundException e) {
        logger.fatal("ReadMapFile(ground)", e);
        JOptionPane.showMessageDialog(theDesktop, getStringChecked("message_file_locks") + "\n\n" + e.getMessage(), getStringChecked("message_error"), JOptionPane.ERROR_MESSAGE);
        System.exit(1);
    }
    return null;
}

private static short[] scanMapFile(String path, int[] leftChunkSides, int[] rightChunkSides) throws FileNotFoundException {
    Scanner scanner = new Scanner(new File(path));
    scanner.useDelimiter(", ");

    int topLeftChunkIndex = leftChunkSides[0];
    int bottomRightChunkIndex = rightChunkSides[rightChunkSides.length - 1];

    short[] tmpMap = new short[chunkWidth * chunkHeight];
    int count = 0;
    int arrayIndex = 0;

    while(scanner.hasNext()){
        if (count >= topLeftChunkIndex && count <= bottomRightChunkIndex){ //within or outside (east and west) of map chunk
            if (count == bottomRightChunkIndex){ //last entry
                tmpMap[arrayIndex] = scanner.nextShort();
                break;
            } else { //not last entry
                if (isInsideMapChunk(count, leftChunkSides, rightChunkSides)){
                    tmpMap[arrayIndex] = scanner.nextShort();
                    arrayIndex++;
                } else {
                    scanner.nextShort();
                }
            }
        } else {
            scanner.nextShort();
        }

        count++;
    }

    scanner.close();
    return tmpMap;
}
private void checkifwithin-dpossiblyreloadchunkmap(){
if(Math.abs(MyClient.characterX-MyClient.chunkX)+10>(MyClient.chunkWidth/5)){//任意数字(10)
Runnable myRunnable=新Runnable(){
公开募捐{
info(“FillMapChunkBuffer已启动”);
short chunkXBuffer=MyClient.characterX;
short chunkYBuffer=MyClient.characterY;
int-topLeftChunkIndex=MyClient.characterX-(MyClient.chunkWidth/2)+((MyClient.characterY-(MyClient.chunkHeight/2))*MyClient.mapWidth);//获取块的左上角坐标
int topRightChunkIndex=topLeftChunkIndex+MyClient.chunkWidth-1;//块的右上角坐标
int[]leftChunkSides=new int[MyClient.chunkHeight];
int[]rightChunkSides=new int[MyClient.chunkHeight];
对于(inti=0;i(MyClient.chunkHeight/5)){//任意数字(10)
//与上述Y代码相同
}
}
公共静态短[]FillGroundBuffer(int[]leftChunkSides,int[]rightChunkSides){
试一试{
返回scanmap文件(“res/images/tiles/MyFirstMap-ground-p.json”,leftChunkSides,righchunksides);
}catch(filenotfounde异常){
logger.fatal(“ReadMapFile(地面)”,e);
显示消息对话框(desktop,getStringChecked(“消息文件锁定”)+“\n\n”+e.getMessage(),getStringChecked(“消息错误”),JOptionPane.error\u消息);
系统出口(1);
}
返回null;
}
私有静态short[]扫描映射文件(字符串路径,int[]leftChunkSides,int[]rightChunkSides)引发FileNotFoundException{
扫描仪=新扫描仪(新文件(路径));
scanner.useDelimiter(“,”);
int topLeftChunkIndex=leftChunkSides[0];
int bottomRightChunkIndex=rightChunkSides[rightChunkSides.length-1];
short[]tmpMap=新的short[chunkWidth*chunkHeight];
整数计数=0;
int-arrayIndex=0;
while(scanner.hasNext()){

如果(count>=topLeftChunkIndex&&count请确保在开始新扫描之前已完成文件扫描

当前,当您的中心距离前一个扫描中心太远时,您将再次开始扫描(可能是在每一帧中)。要解决此问题,请记住,您在开始前扫描了,并相应地增强了您的远离条件

// MyClient.worker represents the currently running worker thread (if any)
if(far away condition && MyClient.worker == null) {
    Runnable myRunnable = new Runnable() {
        public void run(){
            logger.info("FillMapChunkBuffer started.");

            try {
                short chunkXBuffer = MyClient.nextChunkX;
                short chunkYBuffer = MyClient.nextChunkY;

                int topLeftChunkIndex = MyClient.characterX - (MyClient.chunkWidth / 2) + ((MyClient.characterY - (MyClient.chunkHeight / 2)) * MyClient.mapWidth); //get top left coordinate of chunk
                int topRightChunkIndex = topLeftChunkIndex + MyClient.chunkWidth - 1; //top right coordinate of chunk

                int[] leftChunkSides = new int[MyClient.chunkHeight];
                int[] rightChunkSides = new int[MyClient.chunkHeight];

                for (int i = 0; i < MyClient.chunkHeight; i++){ //figure out the left and right index points for the chunk
                    leftChunkSides[i] = topLeftChunkIndex + (MyClient.mapWidth * i);
                    rightChunkSides[i] = topRightChunkIndex + (MyClient.mapWidth * i);
                }

                // no reason for them to be a member of MyClient
                short[] groundLayerBuffer = MyClient.FillGroundBuffer(leftChunkSides, rightChunkSides);
                short[] buildingLayerBuffer = MyClient.FillBuildingBuffer(leftChunkSides, rightChunkSides);


                MyClient.groundLayer = groundLayerBuffer;
                MyClient.buildingLayer = buildingLayerBuffer;
                MyClient.chunkX = chunkXBuffer;
                MyClient.chunkY = chunkYBuffer;
                MyClient.gamePanel.repaint();
                logger.info("FillMapChunkBuffer done.");
            } finally {
                // in any case clear the worker thread
                MyClient.worker = null;
            }
        }
    };

    // remember that we're currently scanning by remembering the worker directly
    MyClient.worker = new Thread(myRunnable);
    // start worker
    MyClient.worker.start();
}
//MyClient.worker表示当前正在运行的工作线程(如果有)
if(远端条件&&MyClient.worker==null){
R