Viele Spiele verwenden die Technik der Delta-Komprimierung, um die gesendete Datenlast zu verringern. Ich verstehe nicht, wie diese Technik tatsächlich die Datenlast senkt?
Angenommen, ich möchte eine Position senden. Ohne Delta-Komprimierung sende ich zum Beispiel eine vector3
mit der genauen Position der Entität (30, 2, 19)
. Mit Delta-Komprimierung sende ich eine vector3
mit kleineren Zahlen (0.2, 0.1, 0.04)
.
Ich verstehe nicht, wie es die Datenlast senkt, wenn beide Nachrichten vector3
- 32 Bit für jedes Float - 32 * 3 = 96 Bit sind!
Ich weiß, dass Sie jedes Float in ein Byte konvertieren und es dann wieder von einem Byte in ein Float konvertieren können, aber es verursacht sichtbare Präzisionsfehler.
networking
compression
user101051
quelle
quelle
Antworten:
Es gibt Situationen, in denen Sie das Senden des vollständigen Spielstatus nicht vermeiden können, z. B. beim Laden eines gespeicherten Multiplayer-Spiels oder wenn eine erneute Synchronisierung erforderlich ist. Aber vollen Zustand zu senden ist in der Regel vermeidbar, und das ist , wo Delta - Codierung kommt in der Regel ist dies. Alle , dass Delta - Kompression ist über; Ihr Beispiel beschreibt diese Situation nicht wirklich. Der Grund, warum Delta-Komprimierung sogar erwähnt wird, ist, dass naive Implementierungen oft eher Status als Deltas senden, da Status normalerweise das ist, was eine naive Spiel-Implementierung sowieso speichert. Deltas sind dann eine Optimierung.
Mit Deltas würden Sie niemals die Positionen von Einheiten senden, die sich überhaupt nicht bewegt haben. Das ist der Geist davon.
Stellen Sie sich vor, wir wären jahrelang Brieffreunde und ich hätte mein Gedächtnis verloren (und alle Ihre Briefe verworfen, nachdem ich sie gelesen hatte). Anstatt einfach wie gewohnt mit Ihrer Serie von Briefen fortzufahren, müssten Sie mir die gesamte Geschichte Ihres Lebens in einem massiven Brief schreiben, damit ich Sie einholen kann.
In Ihrem gegebenen Fall kann es (abhängig von Ihrer Codebasis) möglich sein, eine geringere Anzahl von Bits zum Codieren der Deltas zu verwenden, im Gegensatz zu den großen Bitbereichen, die zum Senden des vollständigen Zustands benötigt werden. Angenommen, die Welt hat einen Durchmesser von vielen Kilometern. Möglicherweise benötigen Sie einen 32-Bit-Float, um Positionen bis auf einen Zentimeter in einer bestimmten Achse genau zu codieren. Angesichts der für Objekte geltenden Maximalgeschwindigkeit, die nur einige Meter pro Tick betragen kann, kann dies jedoch in nur 8 Bit erfolgen (2 ^ 8 = 256, also ausreichend, um maximal 200 cm zu speichern). Dies setzt natürlich eher eine feste als eine Gleitkommazahl voraus ... oder eine Art Halb- / Viertel-Gleitkommazahl wie in OpenGL, wenn Sie keine Probleme mit festen Punkten haben möchten.
quelle
Sie haben das falsche Delta. Sie betrachten das Delta der einzelnen Elemente. Sie müssen an das Delta der gesamten Szene denken.
Angenommen, Ihre Szene enthält 100 Elemente, von denen jedoch nur eines verschoben wurde. Wenn Sie 100 Elementvektoren senden, werden 99 davon verschwendet. Sie müssen wirklich nur 1 senden.
Angenommen, Sie haben ein JSON-Objekt, in dem alle Ihre Elementvektoren gespeichert sind. Dieses Objekt wird zwischen Ihrem Server und Ihrem Client synchronisiert. Anstatt zu entscheiden, "hat sich so und so bewegt?" Sie können einfach Ihren nächsten Spiel-Tick in einem JSON-Objekt generieren, ein erstellen
diff tick100.json tick101.json
und diesen Diff senden. Auf der Clientseite wenden Sie das Diff auf den Vektor Ihres aktuellen Ticks an, und schon sind Sie fertig.Dabei nutzen Sie die jahrzehntelange Erfahrung bei der Erkennung von Unterschieden im Text (oder in der Binärdarstellung!) Und müssen sich nicht darum kümmern, selbst etwas zu verpassen. Idealerweise verwenden Sie jetzt auch eine Bibliothek, die dies hinter den Kulissen tut, um es Ihnen als Entwickler noch einfacher zu machen.
quelle
diff
nach einem ineffizienten Hack. Es ist nicht erforderlich, den JSON-String auf der empfangenden Seite zu belassen, ihn jedes Mal zu patchen und zu deserialisieren. Das Berechnen der Differenz zweier Schlüsselwertwörterbücher ist keine komplizierte Aufgabe. Sie durchlaufen einfach alle Schlüssel und überprüfen, ob die Werte gleich sind. Wenn nicht, fügen Sie sie dem resultierenden Schlüsselwert-Dikt hinzu und senden das Dikt anschließend serialisiert an JSON. Einfach, keine jahrelange Erfahrung erforderlich. Im Gegensatz zu Diffs umfasst diese Methode: 1) keine alten (ersetzten) Daten; 2) spielt besser mit UDP; 3) nicht auf newlines angewiesenSehr oft wird ein anderer Komprimierungsmechanismus in Kombination mit einer Delta-Codierung verwendet, wie zum Beispiel eine arithmetische Komprimierung.
Diese Komprimierungsschemata funktionieren viel besser, wenn die möglichen Werte vorhersehbar gruppiert sind. Bei der Delta-Codierung werden die Werte um 0 gruppiert.
quelle
Sie haben im Großen und Ganzen recht, aber einen wichtigen Punkt übersehen.
Entitäten in einem Spiel werden durch viele Attribute beschrieben, von denen die Position nur eine ist .
Welche Attribute? In einem vernetzten Spiel kann dies, ohne dass Sie zu viel nachdenken müssen, Folgendes umfassen:
Wenn Sie diese einzeln auswählen, können Sie sicher den Fall herbeiführen, dass sie in einem bestimmten Frame, der geändert werden muss, vollständig erneut übertragen werden muss.
Es ändern sich jedoch nicht alle diese Attribute mit der gleichen Geschwindigkeit .
Modell ändert sich nicht? Ohne Delta-Komprimierung muss es trotzdem erneut übertragen werden. Mit Delta-Kompression muss es nicht sein.
Position und Orientierung sind zwei Fälle, die interessanter sind und üblicherweise aus jeweils 3 Schwimmern bestehen. Zwischen zwei gegebenen Frames besteht die Möglichkeit, dass sich nur 1 oder 2 von jedem Satz von 3 Floats ändern können. In einer geraden Linie bewegen? Nicht drehen? Kein Springen? Dies sind alle Fälle, in denen Sie ohne Delta-Komprimierung eine vollständige Neuübertragung durchführen müssen, bei Delta-Komprimierung jedoch nur die Änderungen erneut übertragen müssen.
quelle
Sie haben Recht, dass eine naive Delta-Berechnung für sich genommen, bei der das Ergebnis in der gleichen Größenstruktur wie die Operanden gespeichert und ohne weitere Verarbeitung übertragen wird, keinen Datenverkehr spart.
Es gibt jedoch zwei Möglichkeiten, wie ein gut konzipiertes System, das auf Deltas basiert, Datenverkehr sparen kann.
Erstens ist das Delta in vielen Fällen Null. Sie können Ihr Protokoll so gestalten, dass Sie es gar nicht senden, wenn das Delta Null ist. Dies ist natürlich mit einem gewissen Aufwand verbunden, da Sie angeben müssen, was Sie senden oder nicht, aber insgesamt ist es wahrscheinlich ein großer Gewinn.
Zweitens haben die Deltas normalerweise einen viel kleineren Wertebereich als die ursprünglichen Zahlen. und dieser Bereich wird auf Null zentriert. Dies kann ausgenutzt werden, indem für die meisten Deltas ein kleinerer Datentyp verwendet wird oder indem der gesamte Datenstrom durch einen Allzweck-Komprimierungsalgorithmus geleitet wird.
quelle
Während in den meisten Antworten davon die Rede ist, dass bei der Delta-Codierung nur die Änderungen an den Gesamtstatus gesendet werden, gibt es eine andere Funktion, die als Filter verwendet werden kann, um die Datenmenge zu reduzieren, die im Vollstatus komprimiert werden muss Update auch, was sein kann, woher die Verwirrung in der Frage kommt, wie gestellt.
Bei der Codierung eines Zahlenvektors können Sie in einigen Fällen (z. B. Ganzzahlen, Aufzählungen usw.) eine Codierung mit variablen Bytes für die einzelnen Elemente verwenden. In einigen Fällen können Sie die für jedes Element erforderliche Datenmenge weiter reduzieren Wenn Sie es entweder als laufende Summe oder als Mindestwert und Differenz zwischen jedem Wert und diesem Mindestwert speichern.
Wenn Sie beispielsweise den Vektor codieren möchten, können
{68923, 69012, 69013, 69015}
Sie diesen als Delta-Codierung ausführen{68923, 89, 1, 2}
. Bei Verwendung einer einfachen Codierung mit variablen Bytes, bei der Sie 7 Datenbits pro Byte speichern und mit einem Bit angeben, dass ein weiteres Byte ansteht, würde jedes einzelne Element im Array 3 Bytes für die Übertragung benötigen, jedoch die Delta-codierte Version würde nur 3 Bytes für das erste Element und 1 Byte für die verbleibenden Elemente erfordern; Abhängig von der Art der Daten, die Sie serialisieren, kann dies zu beeindruckenden Einsparungen führen.Dies ist jedoch eher eine Serialisierungsoptimierung und nicht das, was allgemein gemeint ist, wenn wir über "Delta-Codierung" sprechen, wenn es darum geht, beliebige Daten als Teil des Spielzustands (oder Video oder dergleichen) zu streamen. Andere Antworten können dies bereits hinreichend erklären.
quelle
Es ist auch erwähnenswert, dass Kompressionsalgorithmen auf dem Diff besser funktionieren. Wie in anderen Antworten erwähnt, bleiben die meisten Ihrer Elemente zwischen zwei Zuständen gleich, oder die Werte ändern sich um einen kleinen Bruchteil. In beiden Fällen führt die Anwendung eines Komprimierungsalgorithmus auf die Differenz Ihres Zahlenvektors zu erheblichen Einsparungen. Auch wenn Sie nicht gelten keine zusätzliche Logik zum Vektor wie die 0 Elemente zu entfernen.
Hier ist ein Beispiel in Python:
Welches gibt mir:
quelle
Wenn Ihre Position in vector3 gespeichert ist, kann die tatsächliche Entität jedoch nur einige Ganzzahlen gleichzeitig verschieben. Dann wäre es besser, das Delta in Bytes zu senden, als es in Ganzzahlen zu senden.
Anstatt jedes Mal die genaue Position zu senden, senden wir das Delta.
quelle
Delta-Komprimierung ist eine Komprimierung von Delta-codierten Werten. Delta-Codierung ist eine Transformation, die eine unterschiedliche statistische Verteilung von Zahlen erzeugt. Wenn die Verteilung für den gewählten Komprimierungsalgorithmus günstig ist, wird die Datenmenge verringert. Es funktioniert sehr gut in einem System wie einem Spiel, in dem sich Entitäten zwischen zwei Aktualisierungen nur geringfügig bewegen.
Angenommen, Sie haben 100 Objekte in 2D. In einem großen Raster 512 x 512. Betrachtet man zum Beispiel nur ganze Zahlen. Das sind zwei ganze Zahlen pro Entität oder 200 Zahlen.
Zwischen zwei Aktualisierungen ändern sich alle unsere Positionen entweder um 0, 1, -1, 2 oder -2. Es gab 100 Instanzen von 0, 33 Instanzen von 1 und -1 und nur 17 Instanzen von 2 und -2. Das ist ziemlich häufig. Wir wählen Huffman-Codierung für die Komprimierung.
Der Huffman-Baum dafür wird sein:
Alle Ihre Nullen werden als ein einziges Bit codiert. Das sind nur 100 Bits. 66 Werte werden als 3 Bit und nur 34 Werte als 4 Bit codiert. Das sind 434 Bits oder 55 Bytes. Hinzu kommt ein kleiner Aufwand, um unseren Mapping-Baum zu retten, da der Baum winzig ist. Beachten Sie, dass Sie zum Codieren von 5 Zahlen 3 Bits benötigen. Wir haben hier die Fähigkeit getauscht, 1 Bit für '0' zu verwenden, um 4 Bits für '-2' zu verwenden.
Vergleichen Sie dies nun mit dem Senden von 200 beliebigen Zahlen. Wenn sich Ihre Entitäten nicht auf derselben Kachel befinden, ist fast garantiert, dass Sie eine schlechte statistische Verteilung erhalten. Der beste Fall wären 100 eindeutige Zahlen (alle auf demselben X mit unterschiedlichem Y). Das sind mindestens 7 Bits pro Zahl (175 Bytes) und sehr schwer für jeden Komprimierungsalgorithmus.
Die Delta-Komprimierung funktioniert im Sonderfall, wenn sich Ihre Entitäten nur geringfügig ändern. Wenn Sie viele eindeutige Änderungen vorgenommen haben, hilft die Delta-Codierung nicht.
Beachten Sie, dass Delta-Codierung und Komprimierung auch in anderen Situationen mit anderen Transformationen verwendet werden.
MPEG teilt das Bild in kleine Quadrate und wenn sich ein Teil des Bildes bewegt, werden nur die Bewegung und eine Änderung der Helligkeit gespeichert. In einem 25-fps-Film sind viele Änderungen zwischen Bildern sehr gering. Wieder Delta-Codierung + Komprimierung. Funktioniert am besten für statische Szenen.
quelle