„Binarny XML” dla danych gry?

17

Pracuję nad narzędziem do edycji poziomu, które zapisuje dane w formacie XML.

Jest to idealne rozwiązanie podczas programowania, ponieważ wprowadzanie drobnych zmian w formacie danych jest bezbolesne i działa dobrze z danymi drzewiastymi.

Minusem jest jednak to, że pliki XML są raczej rozdęte, głównie z powodu powielania nazw znaczników i atrybutów. Również z powodu danych numerycznych zajmujących znacznie więcej miejsca niż przy użyciu rodzimych typów danych. Mały poziom może łatwo skończyć jako 1Mb +. Chcę znacznie zmniejszyć te rozmiary, zwłaszcza jeśli system ma być używany do gry na iPhonie lub innych urządzeniach o stosunkowo ograniczonej pamięci.

Optymalnym rozwiązaniem dla pamięci i wydajności byłoby przekonwertowanie XML na format binarny. Ale nie chcę tego robić. Chcę, aby format był dość elastyczny. XML bardzo ułatwia dodawanie nowych atrybutów do obiektów i nadawanie im wartości domyślnej, jeśli załadowana jest stara wersja danych. Chcę więc zachować hierarchię węzłów, z atrybutami jako parami nazwa-wartość.

Ale muszę przechowywać to w bardziej zwartym formacie - aby usunąć masowe powielanie nazw znaczników / atrybutów. Może także nadać atrybutom typy rodzime, więc na przykład dane zmiennoprzecinkowe są przechowywane jako 4 bajty na zmiennoprzecinkowe, a nie jako ciąg tekstowy.

Google / Wikipedia ujawniają, że „binarny XML” nie jest nowym problemem - został już rozwiązany wiele razy. Czy ktoś tu ma doświadczenie w zakresie istniejących systemów / standardów? - czy są idealne do użytku w grach - z bezpłatną, lekką i wieloplatformową biblioteką parsera / modułu ładującego (C / C ++)?

Czy powinienem sam odkryć to koło?

A może lepiej zapomnieć o ideale i po prostu kompresować swoje nieprzetworzone dane .xml (powinno się dobrze spakować z kompresją typu zip) i po prostu wziąć obciążenie pamięci / wydajność po załadowaniu?

bluescrn
źródło
1
XML można bardzo dobrze skompresować za pomocą gzip i innych .
ThiefMaster

Odpowiedzi:

18

Często używaliśmy binarnego XML-a do Superman Returns: The Videogame . Mówimy o tysiącach plików. Działało OK, ale szczerze mówiąc nie wydawało się warte wysiłku. Zjadł zauważalną część naszego czasu ładowania, a „elastyczność” XML nie wzrosła. Po pewnym czasie nasze pliki danych miały zbyt wiele dziwnych identyfikatorów, referencje zewnętrzne, które musiały być zsynchronizowane, i inne dziwne wymagania, aby mogły być naprawdę edytowane przez człowieka.

Ponadto XML jest tak naprawdę formatem znaczników, a nie formatem danych. Jest zoptymalizowany pod kątem dużej ilości tekstu z okazjonalnymi tagami. Nie nadaje się do danych o pełnej strukturze. To nie był mój telefon, ale gdyby tak było i wiedziałbym wtedy, co wiem teraz, prawdopodobnie zrobiłbym JSON lub YAML. Oba są na tyle zwięzłe, że nie wymagają kompaktowania, i są zoptymalizowane do reprezentowania danych , a nie tekstu .

hojny
źródło
1
Istnieje binarna wersja JSON o nazwie BSON .
Philipp
12

Przechowuj i edytuj swoje poziomy jako normalne XML, ale silnik gry leniwie upiecz je w binarnym XML podczas ładowania i zapisz binarny XML z powrotem na dysku, aby mógł załadować go następnym razem (jeśli surowy XML nie zmienił się) .

Coś takiego:

data loadXml(xmlFile)
{
    if (xmlFile has changed OR binFile doesn't exist)
    {
        binFile = convertToBinary(xmlFile)
        save(binFile)
    }
    return loadBinaryXml(binFile)
}

W ten sposób uzyskasz to, co najlepsze z obu światów. Po wydaniu musisz tylko upewnić się, że wszystkie pliki binarne tam są.

Peter Alexander
źródło
5

Bufory protokołów Google wydają się właściwą drogą, ale sam ich nie używałem.
http://code.google.com/p/protobuf/

Definiujesz plik .proto, który opisuje format pliku:

message Person {
  required int32 id = 1;
  required string name = 2;
  optional string email = 3;
}

Jest to następnie kompilowane za pomocą narzędzia wiersza poleceń, które generuje klasy C / C ++ do zapisywania i analizowania plików danych binarnych w uprzednio zdefiniowanym formacie danych. Istnieje również kilka rozszerzeń dla różnych języków programowania.

Minusem protokołu ProtocolBuffer jest to, że nie są one formatem zwykłego tekstu. Potrzebujesz narzędzia do ich generowania, czytania i edycji. Nie powinno to jednak stanowić problemu, jeśli używasz ich tylko do wymiany danych między edytorem gier a grą. Nie użyłbym tego do definiowania plików konfiguracyjnych;)

Kompresowanie nieprzetworzonych plików XML powinno również działać. Jakiego rodzaju grę tworzysz? Jeśli jest oparty na poziomie, wszystkie niezbędne zasoby należy załadować tylko raz, gdy poziom jest załadowany.

aktualizacja: Istnieje kilka projektów dla innych języków, takich jak C # do współpracy z ProtocolBuffers:
http://code.google.com/p/protobuf/wiki/ThirdPartyAddOns

Stephen
źródło
Czy serializator nie jest przystosowany do tego rodzaju problemów? Chyba nie, ale nie widzę wyraźnej różnicy. Ale dla mnie ta odpowiedź wydaje się właściwa. Ale również pliki tar / gzip znacznie zmniejszą ich rozmiar (ponieważ jest to tekst, ale myślę, że będzie działać również dla xml), więc może to być „łatwiejsze” rozwiązanie. W każdym razie XML jest łatwym językiem, ale jest bardzo drogi pod względem parsowania / używania pamięci: kiedy używasz XML, powinieneś czytać / pisać tak mało, jak to możliwe.
Jokoon
Jest to interesująca opcja, ale wygląda bardziej na kompletną alternatywę dla używania XML w dowolnym miejscu potoku. Szczerze mówiąc, nie byłbym zbyt entuzjastycznie nastawiony do generowanego kodu - a kolejną komplikacją jest to, że używam C # po stronie narzędzi (cieszę się, że narzędzia mogą nadal pracować z dużymi plikami .XML ). Konwerter XML-> PB może być opcją, chociaż myślę, że wciąż szukam czegoś, co będzie bardziej „binarnym XMLem ogólnego przeznaczenia”, a nie sposobami upieczenia określonych „danych binarnych” (nawet jeśli byłoby to trochę więcej wydajne)
bluescrn
„Używam C # po stronie narzędzi rzeczy” jest kilka projektów dla c #. zaktualizowałem moją odpowiedź.
Stephen
@bluescrn, nie martwiłbym się zbytnio wygenerowanym kodem. Google zapewnia obsługę pierwszej klasy języków C ++, Java i Python. Używają go intensywnie wewnętrznie; wygenerowany kod jest dość solidny. Dużą zaletą PB jest program narzędziowy przeciwko .protoplikowi, który prawie eliminuje problemy z błędną komunikacją. Prototypy są znacznie łatwiejsze do odczytania / utrzymania niż schemat xml, jeśli masz dyscyplinę (i czas), aby używać schematów xml.
deft_code
4

Co z formatem JSON?

http://www.json.org/xml.html

Sven
źródło
Wygląda nieco bardziej kompaktowo niż XML, ale nadal ma główny problem ze zduplikowanymi nazwami atrybutów. Jeśli plik zawiera listę obiektów gry z atrybutami „XPosition”, „YPosition” i „Skala”, ciągi znaków „XPosition” / „YPosition” / „Skala” byłyby duplikowane dla każdego obiektu gry. Jest to główna rzecz, którą obecnie zamierzam „skompresować”
bluescrn
1
@bluescrn: Nie, nie ma tego problemu. Obiekty są jedną strukturą; możesz także użyć tablic [które, po prostu, wyglądają tak]. Oznacza to, że możesz skończyć z czymś takim do przechowywania nazw i właściwości samochodów: "cars":{"ford":[8C,FA,BC,2A,384FFFFF],"holden":[00,00,04,FF,04FF54A9]}możesz nawet pominąć identyfikator „samochodów” i po prostu przejść bezpośrednio do tablicy, jeśli wiesz, gdzie będzie pole samochodów. Można nawet pominąć „Ford” i „gospodarstwo” nazwy, jeśli nie ma potrzeby zapisywania tych danych, dzięki czemu można z: [...,[[8C,FA,BC,2A,384FFFFF],[00,00,04,FF,04FF54A9]]]. Czy robi się bardziej kompaktowy?
doppelgreener
1
@Axidos: Jeśli chcesz, aby znaczniki były nieczytelne i nieustrukturyzowane, równie dobrze możesz po prostu uczynić je binarnymi. Poza tym są to fałszywe oszczędności, chyba że parsujesz nieskompresowane dane w czasie wykonywania (w takim przypadku prawdopodobnie i tak masz problem z wkręceniem), albo w jakiś sposób jesteś ograniczony przez kilkaset bajtów pamięci łańcuchowej podczas parsowania (chyba że jesteś włączony kuchenka mikrofalowa, nie jesteś).
@Joe: bluescrn szuka czytelnego formatu, który nie ma zduplikowanych nazw. Ilustrowałem zdolność JSON do zaoferowania właśnie tego. Zgadzam się jednak całkowicie, że w pewnym momencie możesz równie dobrze zastanawiać się, dlaczego tak niepokoisz się znacznikami.
doppelgreener
4

Użyj JSON.

(Opierając się na odpowiedzi Munificent i głównie w odpowiedzi na twoje obawy wyrażone gdzie indziej)

Wspomniałeś o obawie, że JSON ma problem z marnowaniem elementów nazewnictwa przestrzeni, takich jak XML. Tak nie jest.

JSON jest zbudowany na dwóch strukturach: parach nazwa / wartość ( obiekty ) i uporządkowanych listach wartości ( tablice ). XML jest zbudowany tylko na parach nazwa / wartość.

Jeśli uważasz, że JSON opiera się na obiektach, które czytasz JSON, które są zbudowane tak, aby były samoopisujące i czytelne dla człowieka, jak to poniżej (używając liczb ósemkowych do reprezentowania pojedynczych bajtów):

{
    "some": ...,
    "data": ...,
    "fields": ...,
    "cars": [
        {"name":"greg","cost":8C,"speed":FA,"age":04,"driverID":384FFFFF},
        {"name":"ole rustbucket","cost":00,"speed":00,"age":2A,"driverID":04FF54A9}
    ]
}

Masz jednak możliwość napisania tego w ten sposób, o ile wiesz, gdzie wszystko będzie (i możesz poszukać indeksu 4, a nie obiektu „samochody”, aby uzyskać listę samochodów):

{
    [
        ...,
        ..., 
        ...,
        [["greg",8C,FA,04,384FFFFF],["ole rustbucket",00,00,2A,04FF54A9]],
        ...,
    ]
}

Robi się bardziej zwięzły niż tylko o [, ], ,i swoich wartości?

Dzieje się tak, jeśli chcesz zbliżyć się do czystego strumienia binarnego.

"cars":{"names":["greg","ole rustbucket"],"stream":8CFA04384FFFFF00002A04FF54A9}
or
[["greg","ole rustbucket"],8CFA04384FFFFF00002A04FF54A9]

Po prostu nie strzelaj sobie w nogę, optymalizując zbyt wiele.

doppelgreener
źródło
2

Wiem, że zaakceptowałeś odpowiedź, ale Google zarówno „Fast Infoset” (binarny XML), jak i vtd-xml.

Chociaż ten ostatni (VTD) może nie rozwiązać aspektu kompresji użycia XML, może znacznie przyspieszyć dostęp do węzłów w dużych plikach (znacznie używa „słownika” przesunięć binarnych do przeskakiwania do węzłów i nie tworzy obiektów dla każdego węzła , zamiast tego działa na oryginalny ciąg XML). Dlatego jego wyszukiwanie XML jest [podobno] zarówno szybsze, jak i nie wymaga tak dużo pamięci w trakcie przetwarzania, aby uzyskać dostęp do dokumentu XML lub nim manipulować.

Oba powyższe mają powiązania w popularnych językach (w tym C #).

Twoje zdrowie

Bogaty

Big Rich
źródło
1

Możesz spróbować Karvonite . To ma być zwinne. Jest to struktura utrwalania, która dość dobrze dostosuje się do zmian w twoich danych (co jest miłe w porównaniu do obsługi binarnej twojej jaźni). Właściwie nie jestem pewien, w jaki sposób dane są ustrukturyzowane, ale pliki są znacznie mniejsze niż rozdęte pliki XML. (Zakładam, że zapisuje dane w formacie binarnym zamiast tekstu takiego jak xml)

Jedynym mankamentem, jaki mogę tutaj wymyślić, jest to, że jeśli twoje dane zostaną uszkodzone lub w jakiś sposób popsuty w taki sposób, że Karvonite go nie lubi, jesteś na łasce jego twórców, chyba że zorientujesz się, jak struktura dane działają.

Sposób, w jaki określasz sposób zapisywania / ładowania danych, polega na otwarciu ich edytora trwałości, zaimportowaniu zestawu ze wszystkimi obiektami danych i zaznaczeniu niektórych pól wyboru, aby pokazać, które obiekty mają być obsługiwane i jakie pola / właściwości zapisać.

Może warto spróbować. Odkąd używasz C #, to pasuje do twojego języka, ponieważ działa z XNA (Windows, Xbox360 i Windows Phone 7, który moim zdaniem jest zainteresowany odkąd wspomniałeś o iPhonie?).

Edycja: Właśnie zauważyłem, że używasz tylko C # dla narzędzi. Prawdopodobnie nie pasowałoby to zbyt dobrze do Twojego przepływu pracy. Z jakiegoś powodu miałem XNA w głowie.

Michael Coleman
źródło