Überspringen zum Hauptinhalt

So testen Sie Spielstandmigrationen in Unity anhand früherer Spielerzustände

Originales Persistium-Migrationsdiagramm mit historischen Speicher-Checkpoints, Transformationsschritten und inspizierten Ergebnissen.

Ein praktischer, lokaler Erstprozess zum Proben von Unity speichert Migrationen gegen reale historische Zustandsformen, bevor ein Update die Spieler erreicht.

Clean-Account-Tests verfehlen den wichtigen Defekt

Ein neuer Unity-Build kann perfekt aussehen, wenn er mit einem leeren Profil beginnt. Ein neuer Account hat keine ausgedienten Felder, halbfertige Quests, zwischengespeicherte Katalogreferenzen, alte Inventarstapel, unterbrochene Käufe oder Werte, die nach einem früheren Regelsatz gültig waren. Es beweist daher nur, dass das neue Format einen neuen Zustand erzeugen kann. Es sagt sehr wenig darüber aus, ob das Spiel einen Spieler sicher von der Welt von gestern in die von heute tragen kann.

Migrationsarbeit sollte von einer stärkeren Frage ausgehen: Kann genau dieser historische Zustand in den aktuellen Build eintreten, die Bedeutung beibehalten, die immer noch wichtig ist, und entweder zu einem gültigen aktuellen Zustand werden oder auf eine Weise scheitern, die nützlich ist, um diagnostiziert zu werden? Das ist eine Produktfrage genauso wie eine Serialisierungsfrage. Ein Feld kann erfolgreich deserialisieren, während es immer noch gegen eine Progressionsregel verstößt, auf fehlende Inhalte verweist oder eine Kaufberechtigung ändert. Bytes laden reicht nicht aus.

Dieser Artikel beschreibt einen lokalen Erstprobeprozess. Es ist bewusst kein Versprechen, dass ein Tool jedes Unity-Serialisierungs-Setup, Backend oder Plattformintegration verstehen kann. Unity-Datenmodelle variieren, und ein Spiel kann seine eigene Verschlüsselung, Cloud-Synchronisation, Binärformat oder benutzerdefinierte Adapter hinzufügen. Der dauerhafte Teil ist die Testdisziplin: historische Formen bewahren, sie vor der Mutation kopieren, den Migrationspfad explizit machen und Spielinvarianten nach jeder Grenze überprüfen.

Bauen Sie ein historisches Staatscorpus

Behandle alte Speicher als versionierten Korpus, nicht als lose Dateien, die im Chat herumgereicht werden. Jede Vorrichtung benötigt eine stabile Kennung, die Version, die sie produziert hat, ein kurzes Szenario-Etikett, eine Formatdeklaration und eine Datenschutzklassifizierung. Das Szenario-Etikett ist wichtig, weil eine Migrationssuite sinnvolle Zustände enthalten sollte, nicht nur zufällige JSON. Beispiele sind ein neuer Player, ein Player an einer Kapitelgrenze, ein Inventar an seiner Stapelgrenze, ein unterbrochenes Tutorial, eine alte Ereignisbelohnung, ein Benutzer mit optionalem kosmetischem Inhalt und ein Profil, das absichtlich eine ungültige Kombination enthält.

Verwende unveränderliche Arbeitskopien. Der Testläufer sollte die Quellhalterung niemals überschreiben, während er eine Migration versucht. Kopieren Sie die Vorrichtung in ein temporäres Arbeitsverzeichnis, notieren Sie ihre Prüfsumme oder den Inhalts-Hash, führen Sie den Adapter aus und behalten Sie die resultierende Ausgabe separat. Dies macht ein überraschendes Ergebnis zu Beweisen, die jemand anderes wiedergeben kann. Es verhindert auch, dass ein Test seine eigenen Eingaben leise verbessert, bis das ursprüngliche Problem verschwindet.

Halten Sie Produktionsplayerdaten aus einem allgemeinen Verzeichnis von Entwicklungsvorrichtungen heraus. Ein historischer Korpus kann synthetisch, einvernehmlich, sorgfältig redigiert oder aus deterministischen Szenarien generiert werden. Der Schlüssel ist, dass er die strukturellen und semantischen Randfälle beibehält, die wichtig sind. Datenschutz ist kein nachträglicher Einfall: Entfernen Sie direkte Identifikatoren, Geräteidentifikatoren, Kontaktinformationen, Zahlungstoken, Rohbelege und Geheimnisse, bevor eine Vorrichtung allgemein zugänglich wird.

Zeichnen Sie den Migrationsgraphen, bevor Sie einen Konverter schreiben

Versionsnummern sind kein Migrationsplan. Ein Spiel kann von 1 zu 2, 2 zu 3 und 3 zu 4 wechseln und dann einem Spieler begegnen, der direkt von 1 zu 4 übersprungen ist. Es kann einen Hotfix versenden, der nur einen verschachtelten Inventardatensatz ändert, oder ein Feld zurückziehen, das alte Inhalte noch erwarten. Ein Graph macht diese Auswahl sichtbar: jeder Knoten ist ein Speicherschema oder eine semantische Version; jede Kante ist eine explizite Transformation oder eine absichtliche Ablehnung; jede Kante hat Vorbedingungen und Nachbedingungen.

Entscheiden Sie für jede unterstützte Startversion, ob die benachbarten Spielketten transformiert, eine direkte Transformation verwendet oder ein serviceseitiges Upgrade erfordert, bevor der Client geladen wird. Überlassen Sie nicht übersprungene Versionen, was auch immer passiert, wenn Felder fehlen. Eine Kette kann sicherer sein, wenn jeder Schritt klein und getestet ist. Eine direkte Transformation kann sicherer sein, wenn Zwischenformate nie beobachtbar sein sollten.

Ursprünglicher Migrationsgraph mit fünf Zustandsknoten, die durch explizit unterstützte Konversionspfade verbunden sind.
Ein Migrationsgraph macht Versionssprünge, unterstützte Pfade und Inspektionspunkte sichtbar, bevor der Code sie verbirgt.

Der Graph sollte mehr als den serialisierten Typ enthalten. Inhaltskatalogabhängigkeiten, Konfigurationsversionen, Feature-Flags, die die Interpretation beeinflussen, und die aktuelle Anwendungsversion. Ein Speicher kann syntaktisch aktuell sein, aber ungültig, weil ein referenziertes Element nicht mehr existiert oder eine Regel die Bedeutung geändert hat. Das sind Kompatibilitätsgrenzen, keine störenden Randfälle.

Machen Sie Save-Load Round Trips beobachtbar

Sobald eine Vorrichtung in den Migrationspfad eintritt, führen Sie eine vollständige Rundreise durch: Laden Sie die historische Eingabe, migrieren Sie sie, konstruieren Sie das In-Game-Modell, speichern Sie es mit dem aktuellen Schreiber, laden Sie dann die aktuelle Ausgabe erneut. Die erste Ladung prüft, ob ein historischer Datensatz in das System gelangen kann. Die zweite Ladung prüft, ob der aktuelle Schreiber ein stabiles, selbstkonsistentes Ergebnis erstellt hat. Vergleichen Sie den Zustand nach der ersten und zweiten Ladung nach semantischen Regeln, nicht nur Rohbytegleichheit.

Rohe Gleichheit ist oft die falsche Behauptung. Neue Standardwerte können hinzugefügt werden, Feldreihenfolge kann sich ändern, Zeitstempel können aktualisiert werden, und Sammlungen können stabile Ordnung erhalten. Definieren Sie einen Normalisator für Werte, die absichtlich nicht semantisch sind, dann vergleichen Sie den Rest. Der Normalisator selbst verdient eine Überprüfung; er darf nicht zu einer pauschalen Ignorierliste werden, die echten Verlust verbirgt. Ein guter Vergleich berichtet, welche Felder hinzugefügt, entfernt, transformiert oder absichtlich ignoriert wurden.

Das Testergebnis sollte den ersten ungültigen Zustand benennen. Wenn eine Migrationskette drei Übergänge hat, melden Sie nicht nur, dass die endgültige Last ausgefallen ist. Erfassen Sie den Zustand nach jedem Kontrollpunkt, validieren Sie ihn und stoppen Sie ihn an der frühesten Grenze, die eine Invariante verletzt. Diese Lokalisierung wandelt einen vagen Bericht "alt gespeichert gebrochen" in einen bestimmten Migrationsrand, Fixture und Zustandsunterschied um.

Express spielspezifische Invarianten

Ein allgemeines Migrationsgeschirr kann nicht wissen, was Progression in einem bestimmten Spiel bedeutet. Das Spiel muss seine eigenen Invarianten liefern. Schreiben Sie sie in einer Sprache, die mit den Ergebnissen des Spielers in Verbindung steht: ein Level liegt im aktuellen Bereich; eine Inventarmenge ist nicht negativ und begrenzt; jeder eigene Gegenstand hat eine bekannte Definition oder einen expliziten Legacy-Ausfallback; ein abgeschlossenes Tutorial bleibt abgeschlossen; Währung ist endlich; eine Quest-Referenz gehört zu einem aktuellen Katalog; eine Berechtigung ist einzigartig; ein Welt-Seed und ein generierter Welt-Zustand stimmen überein.

Getrennte Invarianten in Schichten. Strukturelle Invarianten beantworten, ob erforderliche Felder und Typen existieren. Referenzielle Invarianten beantworten, ob IDs auf bekannte Definitionen verweisen. Domäneninvarianten beantworten, ob Werte den Spielregeln entsprechen. Sicherheitsinvarianten beantworten, ob Daten endlich, begrenzt und sicher angezeigt werden können. Wenn ein Fixture ausfällt, teilt die Schicht dem Team mit, ob es einen Serializerwechsel, einen Katalogadapter, eine Regelsatzentscheidung oder eine Wiederherstellungsrichtlinie benötigt.

Verwenden Sie deterministische Mutationen, damit der Korpus härter arbeitet. Beginnen Sie mit einer bekannten Vorrichtung und entfernen Sie absichtlich ein Feld, duplizieren Sie eine Inventar-ID, ersetzen Sie eine Katalogreferenz, überspringen Sie eine Version, ordnen Sie eine Aktionsliste neu an oder ändern Sie eine Grenzgröße. Jede Mutation sollte ein erwartetes Ergebnis haben: Migration mit einem dokumentierten Standard, Ablehnung mit einer diagnostischen Kategorie, Quarantäne für die Wiederherstellung oder erfordern Sie eine vom Eigentümer genehmigte Datenreparatur. „Es stürzt nicht ab. ist kein ausreichendes erwartetes Ergebnis.

Reduzieren Sie ein ausfallendes Save, ohne den Bug zu verlieren

Große Spielerzustände sind schwer zu verstehen. Wenn ein Fixture Hunderte von Feldern enthält und eine Migration fehlschlägt, kann ein Entwickler Stunden damit verbringen, nicht verwandte Daten zu lesen. Die Gegenbeispiel-Minimierung reduziert das Fixture, während die verletzte Invariante erhalten bleibt. Eine Feldgruppe entfernen, dieselbe deterministische Migration wiederholen und die Entfernung nur dann beibehalten, wenn der Fehler weiterhin auftritt. Dann Partitionssammlungen, Werte in Richtung Grenzen verkleinern und den Aktionsverlauf reduzieren, wenn das Wiedergabeverhalten wichtig ist.

Originales Gegenbeispiel-Reduktionsvisual, das einen großen Zustand zeigt, der durch wiederholbare Partitionen zu einer minimalen Regressionsvorrichtung verengt wird.
Der Reduktionsprozess bewahrt den invarianten Fehler, während der nicht verwandte Zustand entfernt wird.

Reduktion erfordert Reproduzierbarkeit. Locken Sie den Fixture-Hash, die Migrationsversion, die Adapterversion, den deterministischen Seed, den relevanten Konfigurations-Snapshot und die erwartete Invariante. Wenn sich die Umgebung während jedes Wiederholungsversuchs ändert, kann ein Reducer einen kleineren Zustand für ein anderes Problem erzeugen. Halten Sie ein Wiederholungsmanifest mit diesen Eingaben und dem beobachteten Ergebnis. Die Ausgabe ist nicht nur eine kleinere Datei, sondern ein Regressionsfall, den jemand anderes ausführen kann.

Erzeugte Regressionsvorrichtungen sollten denselben Überprüfungspfad wie von Hand verfasste eingeben. Geben Sie jeder Vorrichtung einen stabilen Namen, eine kurze Erklärung des Fehlers, die Invariante, die sie bewahrt, und eine erwartete Klassifizierung. Eine Regressionsvorrichtung ist wertvoll, weil sie eine zuvor überraschende Grenzroutine bildet. Sie sollte nicht zu einer versteckten Quelle für Produktionsplayerdaten oder zu einem Sammelsurium für unerklärte Fehler werden.

Setzen Sie die Probe in CI, ohne es laut zu machen

Führen Sie eine schnelle repräsentative Untermenge für jede relevante Änderung aus: aktuelle Migrationskanten, risikoreiche historische Zustände und einen kleinen Satz deterministischer Mutationen. Führen Sie den vollständigen Korpus für einen geplanten Job, einen Release-Kandidaten oder eine Änderung aus, die Serialisierung, Progression, Kataloginterpretation, Cloud-Synchronisierung oder Speicherformatcode berührt. Teilen Sie nach Fixture-Familie oder Migrationskante, anstatt jeden Test durch eine lange Sequenz zu senden.

CI-Ausgabe sollte prägnant, aber inspizierbar sein. Fixture ID, Start- und Zielversionen, Migrationsrand, invarianter Layer, erster ungültiger Checkpoint und ein Pfad zum redigierten Artefakt oder Diff angeben. Vollprofilinhalte in Protokollen vermeiden. Wenn ein Fehler neu ist, die minimalen Informationen behalten, die erforderlich sind, um ihn lokal zu reproduzieren. Wenn es erwartet wird, notieren Sie diese Klassifizierung, damit eine Veröffentlichung ein bekanntes Risiko nicht stillschweigend normalisiert.

In der Veröffentlichungsentscheidung sollte angegeben werden, was die Beweise bedeuten. Bestehende Migrationstests stützen einen begrenzten Anspruch: Der getestete Korpus und die deklarierten Pfade haben sich in dieser Umgebung wie erwartet verhalten. Sie beweisen nicht, dass sich jeder Spieler, jedes Gerät oder jeder externe Dienst identisch verhält. Teams benötigen weiterhin Rollback-Pläne, Kundensupport-Handhabung und Überwachung, die die Privatsphäre respektieren.

Praktische Checkliste

  • Bewahren Sie einen versionierten historischen Korpus mit Szenario-Etiketten und Datenschutzklassifikationen auf.
  • Kopieren Sie Eingaben vor der Migration und bewahren Sie Hashes für Quellen- und Ausgabeartefakte auf.
  • Draw unterstützte Migrationskanten, einschließlich übersprungener Versionsrichtlinien und expliziter Ablehnungen.
  • Validieren Sie strukturelle, Referenz-, Domänen- und Sicherheitsinvarianten nach jedem Checkpoint.
  • Round-Trip migrierte Ausgabe durch den aktuellen Autor und Leser.
  • Verwenden Sie deterministische Mutationen, um fehlende Felder, doppelte Datensätze, pensionierte Inhalte und Grenzwerte zu testen.
  • Reduzieren Sie reproduzierbare Fehler in überprüften Regressionsvorrichtungen mit Wiederholungsmanifesten.
  • Führen Sie risikoorientierte Untergruppen kontinuierlich und den vollständigen Korpus aus, bevor Release-sensitive Änderungen vorgenommen werden.
  • Bewahren Sie Protokolle und Artefakte auf; verwandeln Sie CI nicht in ein Player-Data-Archiv.

Häufig gestellte Fragen

Brauchen wir echte Spielerspeicher, um die Migration zu testen?

Nein. Reale historische Formen sind nützlich, aber der Korpus kann synthetisch oder sorgfältig redigiert sein. Es kommt darauf an, die schwierigen Zustandskombinationen zu erhalten: ausgediente Inhalte, lange Progression, unterbrochene Übergänge, optionale Daten und Werte an sinnvollen Grenzen. Wenn Produktionsdaten verwendet werden, um einen Randfall zu entdecken, leiten Sie eine sichere Befestigung ab, die nur die getestete Eigenschaft behält.

Sollte jede alte Version für immer unterstützt werden?

Nicht unbedingt. Support ist eine Produkt- und Betriebsentscheidung. Wichtig ist, die Grenze explizit zu machen: welche Versionen migrieren, welche ein früheres Update erfordern, welche abgelehnt werden und wie der Spieler informiert wird. Ein undokumentierter Zufallspfad ist die am wenigsten sichere Option, da er weder zuverlässig unterstützt noch eindeutig abgelehnt wird.

Kann ein Serializer-Test Gameplay-Tests ersetzen?

Serializer-Tests stellen fest, dass Daten nach deklarierten Formen gelesen und geschrieben werden können. Gameplay- und Domänentests stellen fest, dass der resultierende Zustand im aktuellen Regelsatz sinnvoll ist. Eine robuste Migrationsprobe erfordert beides sowie eine Überprüfung von Inhalts- und Konfigurationsreferenzen, die die Bedeutung der gespeicherten Werte beeinflussen.

Welche Rolle spielt Persistium dabei?

Persistium ist eine in der Entwicklung befindliche lokale Zuverlässigkeitsproduktfamilie. Die Anleitung in dieser Anmerkung ist praktische technische Beratung, nicht eine Behauptung, dass ein öffentliches Persistium-Produkt verfügbar ist oder dass es echte Kundendaten diagnostiziert hat. Seine Kontinuitätsrichtung untersucht die Art des oben beschriebenen expliziten Migrations-, Wiederholungs-, Vergleichs- und Reduktionsworkflows.

Verbundenes SystemEntdecken Sie Persistium