如何使用Java正确压缩和转换TiledMap数据?

时间:2011-10-27 06:45:37

标签: java xml gzip base64 slick2d

所以我目前正在为名为Slick2dTiledMapPlus库创建一个库扩展。它旨在为使用Tiled的人们提供更大的支持,并更快地访问数据等。

该库的目标之一是提供动态地图编辑,因此能够将新地图写入流。  我今天和昨天实施了这个,现在需要你的帮助。我的代码有问题。

因此,基本问题是,TiledMap解析器/编辑器的XML层数据格式错误无法读取。  我已经经历了许多教程,试图将数据压缩为 GZIP压缩,BASE64格式。我最终使用了this和GZIP压缩选项。  但是,每次我压缩数据等。它总是比Tiled2d编辑器输出更大,它输出损坏的数据。这是为什么?

指向文件的链接:

A Tiled Map automatically generated by the TiledMap editor

A Tiled Map generated from the above TiledMap, using my library

Snippet of the code from my library which isn't working

Alternatively you can view them all formatted here

3 个答案:

答案 0 :(得分:1)

您正在使用的方法是设置一个输出流管道,如下所示:

ObjectOutputStream - > GZIPOutputStream - > Base64OutputStream

您不希望拥有第一个ObjectOuputStream,因为它正在序列化JAVA字节数组对象。您应该直接使用字节提供GZIpOuputStream。如果找不到合适的方法,使用apache commons编解码器很容易设置自己的方法:

public String compressAndEncode(byte[] data) throws Exception {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    GZIPOutputStream gz = new GZIPOutputStream(out);
    ByteArrayInputStream in = new ByteArrayInputStream(data);
    int c;
    while ((c = in.read()) != -1)
        gz.write(c);
    gz.finish();
    Base64 b = new Base64(0);
    return b.encodeToString(out.toByteArray());
}

答案 1 :(得分:1)

谢谢Liam我实际上一直在跟踪你能找到的所有可能的东西,你已经写好了,而你在TiledMapPlus中的代码终于为我解决了这个问题。

这是从利亚姆代码中正确使用的解决方案:

            Element layer = doc.createElement("layer");
        layer.setAttribute("name", "layer");
        layer.setAttribute("width", width);
        layer.setAttribute("height", height);



            Element data = doc.createElement("data");

            ByteArrayOutputStream os = new ByteArrayOutputStream();                
            for(int tileY = 0;tileY<layerHeight;tileY++){
                for(int tileX = 0;tileX<layerWidth;tileX++){
                    int tileGID = this.data[tileX][tileY];
                    os.write(tileGID);
                    os.write(tileGID << 8);
                    os.write(tileGID << 16);
                    os.write(tileGID << 24);
                }
            }
            os.flush();
            String compressedData = Base64.encodeBytes(os.toByteArray(),Base64.DONT_BREAK_LINES|Base64.GZIP|Base64.ENCODE);
            data.appendChild(doc.createTextNode(compressedData));               
            data.setAttribute("encoding", "base64");
            data.setAttribute("compression","gzip");

            layer.appendChild(data);

        mapElement.appendChild(layer);

我使用相同的数据生成技术并传入我想要的任何ID号,它完全接受并编码它!

答案 2 :(得分:0)

我不能说四十二的答案的价值。如果有效,请回复说。我已经在一个正在编写的游戏中使用了这个问题,问题在于你正在使用的格式。

当你创建一个TMX文件,其中包含TiledMapPlus和slick2d所需的压缩和gziped数据时,你不要使用<tiled>标签,如一般的非压缩文件。它的反直觉,但它是如何工作的。

要在<data>标记中创建数据,您需要使用32位整数创建每个gid的字符串/流,然后将其转换为UTF-8,然后对其进行压缩和编码。

以下是我在网络上找到的一个例子:

                Element data = doc.createElement("data");
            data.setAttribute("encoding", "base64");
            data.setAttribute("compression", "gzip");

                String bytestring = new String();
                for (int x = 0; x < w; x++) {                       
                    for (int y = 0; y < h; y++) {
                        switch(this.data[x][y]){

                            case 0: bytestring += "1000";
                                break;
                            case 1: bytestring += "2000";
                                break;
                            case 2: bytestring += "3000";
                                break;
                            case 3: bytestring += "4000";
                                break;
                            case 4: bytestring += "5000";
                                break;
                            case 5: bytestring += "6000";
                                break;
                            case 6: bytestring += "7000";
                                break;
                            case 7: bytestring += "8000";
                                break;
                            case 8: bytestring += "9000";
                                break;

                        }
                    }
                }

                Text value = doc.createTextNode(compress(bytestring));
                data.appendChild(value); 

压缩和编码完成如下:

private static String compress(String str){

    byte byteAry[] = null;

    try{
        byteAry = str.getBytes("UTF-8");    
    }catch( UnsupportedEncodingException e){
        System.out.println("Unsupported character set");
    }

    for(int i = 0; i < byteAry.length; i++) {
        if(byteAry[i] == 48)
            byteAry[i] = 0;
        if(byteAry[i] == 49)
            byteAry[i] = 1;
        if(byteAry[i] == 50)
            byteAry[i] = 2;
        if(byteAry[i] == 51)
            byteAry[i] = 3;
        if(byteAry[i] == 52)
            byteAry[i] = 4;
        if(byteAry[i] == 53)
            byteAry[i] = 5;
        if(byteAry[i] == 54)
            byteAry[i] = 6;
        if(byteAry[i] == 55)
            byteAry[i] = 7;
        if(byteAry[i] == 56)
            byteAry[i] = 8;
        if(byteAry[i] == 57)
            byteAry[i] = 9;
    }
    ByteArrayOutputStream buffer = new ByteArrayOutputStream();

    try {
        OutputStream deflater = new GZIPOutputStream(buffer);
        deflater.write(byteAry);
        deflater.close();
    }catch (IOException e) {
        throw new IllegalStateException(e);
    }
    String results = Base64.encodeBase64String(buffer.toByteArray());
    return results;
}   

现在,这是一个高度相关的更高级的问题。在上面的示例中,您可以看到每个GID由32位字符串表示,例如1000.这些字符串直接与包含的tileset文件相关联。我有一个问题,我似乎可以使用这种技术超过GID 9(显示为9000)。我相信这与ByteStream本身有关。如果我输入1100,它会崩溃为该图块的GID的空值(当读取文件时),即使图块集中有20个左右的图块。因此,对于任何2位数字的编码和压缩后,返回的内容有问题。这似乎是特定于java的,因为使用obj-c工作的人似乎没有遇到同样的问题。

非常感谢任何帮助。