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

发布于 2024-12-11 21:27:11 字数 1217 浏览 0 评论 0 原文

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

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

所以基本问题是,XML 层数据格式错误,并且 TiledMap 解析器/编辑器无法读取。 我已经阅读了大量教程,尝试将数据压缩为 GZIP 压缩的 BASE64 格式。我最终求助于使用 this 和 GZIP 压缩选项。 然而,每次我压缩数据等时,它总是输出比 Tiled2d 编辑器更大的输出,并且输出损坏的数据。这是为什么呢?

文件链接:

由 TiledMap 自动生成的 Tiled Map编辑器

使用我的库从上述 TiledMap 生成的平铺地图

我的库中无法正常工作的代码片段

或者,您可以在此处查看所有格式的内容

So I'm currently creating a library extension to the Slick2d library called TiledMapPlus. It aims to provide greater support to the people using Tiled, and faster access to data etc.

One of the aims of the library was to provide dynamic map editing, therefore the ability to write a new map to a stream.
I implemented this today and yesterday and now need your help. There is something wrong with my code.

So the basic problem is, the XML layer data is malformed, and cannot be read by the TiledMap parser/editor.
I have gone through numerous tutorials trying to compress the data into a GZIP compressed, BASE64 format. I have finally resorted to using this with the GZIP compress option.
However every time I compress the data etc. It always outputs larger than the Tiled2d editor does and it outputs corrupt data. Why is this?

Links to Files:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

似最初 2024-12-18 21:27:11

谢谢 Liam,我实际上一直在关注您所写的所有可能的内容,正是您在 TiledMapPlus 中的代码最终为我解决了这个问题。

这是从 Liam 的代码中正确工作的解决方案:

            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 号,它完美地接受和编码!

Thanks Liam I've actually been following every possible thing I can find that you've written and it was your code in TiledMapPlus that finally solved this for me.

Here is the solution that works correctly taken from Liam's code:

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

I am using my same data generation techniques and passing in any id number I want and it is accepting and encoding it perfectly!

不一样的天空 2024-12-18 21:27:11

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

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

The method you are using is setting up a pipeline of output streams like this:

ObjectOutputStream -> GZIPOutputStream -> Base64OutputStream

You will not want to have the first ObjectOuputStream, since it is serializing a JAVA byte array object. You should just feed the GZIpOuputStream with the bytes directly. If you cannot find a suitable method, it is very easy to set up your own using apache commons codec:

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());
}
魂归处 2024-12-18 21:27:11

我无法评价四十二的答案的价值。如果有效,请回复说这样。我已经在我编写的游戏中得到了这个工作,问题出在你正在使用的格式上。

当您创建一个 TMX 文件,其中数据按照 TiledMapPlus 和 slick2d 的要求进行压缩和 gzip 压缩时,您不要像一般的非压缩文件一样使用 标签。它违反直觉,但这就是它的工作原理。

要在 标记中创建数据,您需要使用 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)表示。它们直接与包含的图块集文件关联。我遇到的问题是,我似乎可以使用此技术来超越 GID 9(显示为 9000)。我相信这与 ByteStream 本身有关。如果我输入 1100,它会作为该图块的 GID 的空值(读入文件时)崩溃,即使图块集中有 20 个左右的图块。因此,任何 2 位数字在编码和压缩后返回的内容都会有问题。这似乎是 java 特有的,因为使用 obj-c 进行工作的人似乎没有遇到同样的问题。

任何帮助将不胜感激。

I cant speak to the value of the answer from forty-two. If it worked please respond saying so. I have gotten this working in a game Im writing and the problem is in the format you're using.

When you create a TMX file that has the data compressed and gziped as is required for TiledMapPlus and slick2d you DONT use the <tiled> tags like the general non-compressed files. Its counter intuitive but thats how it works.

To create the data in the <data> tag you need to create a string/stream of each gid using a 32bit integer and then convert it to UTF-8 and then compress and encode it.

Here is an example that I found somewhere on the web:

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

The compression and encoding are done like this:

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

Now here is a more advanced question that is highly related. In the above example you can see each GID is being represented by a 32bit string such as 1000. These associate directly with the included tileset file. I am having a problem in that I can seem to use this technique to go above GID 9 (show as 9000). I believe this is something to do with the ByteStream itself. If I enter 1100 it crashes as a null value for the GID of that tile (when reading the file in) even though the tileset has 20 or so tiles in it. So there is something wrong w/ what gets returned after the encoding and compression for any 2 digit number. This seems rather java specific as the people doing obj-c work with this dont seem to hit this same problem.

Any help would be greatly appreciated.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文