Как правильно сжимать и конвертировать данные TiledMap с помощью Java? - PullRequest
0 голосов
/ 27 октября 2011

Итак, в настоящее время я создаю расширение библиотеки для библиотеки Slick2d под названием TiledMapPlus .Она направлена ​​на то, чтобы обеспечить большую поддержку людям, использующим Tiled, и более быстрый доступ к данным и т. Д.

Одной из целей библиотеки было обеспечение динамического редактирования карты, поэтому возможность записи новой карты в поток,Я реализовал это сегодня и вчера, и теперь нуждаюсь в вашей помощи.С моим кодом что-то не так.

Таким образом, основная проблема заключается в том, что данные слоя XML имеют искаженный , и не может быть прочитан анализатором / редактором TiledMap.Я прошел многочисленные уроки, пытаясь сжать данные в GZIP сжатый, формат BASE64 .Я наконец прибег к использованию this с опцией сжатия GZIP.Однако каждый раз, когда я сжимаю данные и т. Д. Он всегда выводит больше, чем редактор Tiled2d, и выводит поврежденные данные.Почему это так?

Ссылки на файлы:

Плиточная карта, автоматически создаваемая редактором TiledMap

Tiled Map, сгенерированная из вышеупомянутого TiledMap, с использованием моей библиотеки

Фрагмент кода из моей библиотеки, который не работает

В качестве альтернативы выможете просмотреть их все в формате здесь

Ответы [ 3 ]

1 голос
/ 26 февраля 2012

Спасибо, Лиам, я действительно следил за всеми возможными вещами, которые я могу найти, что вы написали, и именно ваш код в 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);

Я использую те же методы генерации данных и передаю любой номер идентификатора, который хочу, и он прекрасно принимает и кодирует его!

1 голос
/ 28 октября 2011

Метод, который вы используете, устанавливает конвейер выходных потоков следующим образом:

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());
}
0 голосов
/ 26 февраля 2012

Я не могу говорить о значении ответа от сорока двух. Если это сработало, пожалуйста, ответьте так. Я получил эту работу в игре, которую я пишу, и проблема в том формате, который вы используете.

Когда вы создаете файл TMX, в котором данные сжаты и сжаты, как требуется для TiledMapPlus и slick2d, вы НЕ ДОЛЖНЫ использовать теги <tiled>, как обычные несжатые файлы. Его счетчик интуитивно понятен, но вот как это работает.

Чтобы создать данные в теге <data>, вам нужно создать строку / поток каждого gid, используя 32-битное целое число, а затем преобразовать их в 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 плиток. Так что что-то не так с возвращаемым после кодирования и сжатия для любого двухзначного числа. Это кажется довольно специфичным для Java, так как люди, работающие с obj-c, не сталкиваются с этой же проблемой.

Любая помощь будет принята с благодарностью.

...