In diesem Leitfaden erfahren Sie, wie Sie gespeicherte Spiele mithilfe der Snapshots API der Google Play Games-Dienste implementieren. Die APIs befinden sich in den
com.google.android.gms.games.snapshot
und
com.google.android.gms.games
Paketen.
Hinweis
Weitere Informationen zu dieser Funktion finden Sie in der Übersicht zu gespeicherten Spielen.
- Aktivieren Sie die Unterstützung für gespeicherte Spiele für Ihr Spiel in der Google Play Console.
- Laden Sie das Codebeispiel für gespeicherte Spiele auf der Seite mit Android-Beispielen herunter und sehen Sie es sich an.
- Machen Sie sich mit den Empfehlungen der Qualitäts Checkliste vertraut.
Snapshots-Client abrufen
Bevor Sie die Snapshots API verwenden können, muss Ihr Spiel zuerst ein
SnapshotsClient
Objekt abrufen. Rufen Sie dazu die
Games.getSnapshotsContents()
Methode auf und übergeben Sie die Aktivität.
Gespeicherte Spiele anzeigen
Sie können die Snapshots API überall dort in Ihr Spiel einbinden, wo Spieler die Möglichkeit haben, ihren Fortschritt zu speichern oder wiederherzustellen. Ihr Spiel kann eine solche Option an bestimmten Speicher- oder Wiederherstellungspunkten anzeigen oder Spielern ermöglichen, den Fortschritt jederzeit zu speichern oder wiederherzustellen.
Sobald Spieler die Option zum Speichern oder Wiederherstellen in Ihrem Spiel auswählen, kann Ihr Spiel optional einen Bildschirm aufrufen, auf dem Spieler Informationen für ein neues gespeichertes Spiel eingeben oder ein vorhandenes gespeichertes Spiel zum Wiederherstellen auswählen können.
Um die Entwicklung zu vereinfachen, bietet die Snapshots API eine Standardbenutzeroberfläche für die Auswahl gespeicherter Spiele, die Sie sofort verwenden können. Über die Benutzeroberfläche für die Auswahl gespeicherter Spiele können Spieler ein neues gespeichertes Spiel erstellen, Details zu vorhandenen gespeicherten Spielen aufrufen und zuvor gespeicherte Spiele laden.
So starten Sie die Standardbenutzeroberfläche für gespeicherte Spiele:
- Rufen Sie
SnapshotsClient.getSelectSnapshotIntent()auf, um einenIntentzum Starten der Standardbenutzeroberfläche für die Auswahl gespeicherter Spiele abzurufen. - Rufen Sie
startActivityForResult()auf und übergeben Sie diesenIntent. Wenn der Aufruf erfolgreich ist, zeigt das Spiel die Benutzeroberfläche für die Auswahl gespeicherter Spiele zusammen mit den von Ihnen angegebenen Optionen an.
Hier ein Beispiel für das Starten der Standardbenutzeroberfläche für die Auswahl gespeicherter Spiele:
private static final int RC_SAVED_GAMES = 9009; private void showSavedGamesUI() { SnapshotsClient snapshotsClient = PlayGames.getSnapshotsClient(this); int maxNumberOfSavedGamesToShow = 5; Task<Intent> intentTask = snapshotsClient.getSelectSnapshotIntent( "See My Saves", true, true, maxNumberOfSavedGamesToShow); intentTask.addOnSuccessListener(new OnSuccessListener<Intent>() { @Override public void onSuccess(Intent intent) { startActivityForResult(intent, RC_SAVED_GAMES); } }); }
Wenn der Spieler ein neues gespeichertes Spiel erstellt oder ein vorhandenes gespeichertes Spiel lädt, sendet die Benutzeroberfläche eine Anfrage an die Play Games-Dienste. Wenn die Anfrage erfolgreich ist,
geben die Play Games-Dienste Informationen zum Erstellen oder Wiederherstellen des gespeicherten Spiels zurück
über den
onActivityResult()-Callback. Ihr Spiel kann diesen Callback überschreiben, um zu prüfen, ob bei der Anfrage Fehler aufgetreten sind.
Das folgende Code-Snippet zeigt eine Beispielimplementierung von
onActivityResult():
private String mCurrentSaveName = "snapshotTemp"; /** * This callback will be triggered after you call startActivityForResult from the * showSavedGamesUI method. */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if (intent != null) { if (intent.hasExtra(SnapshotsClient.EXTRA_SNAPSHOT_METADATA)) { // Load a snapshot. SnapshotMetadata snapshotMetadata = intent.getParcelableExtra(SnapshotsClient.EXTRA_SNAPSHOT_METADATA); mCurrentSaveName = snapshotMetadata.getUniqueName(); // Load the game data from the Snapshot // ... } else if (intent.hasExtra(SnapshotsClient.EXTRA_SNAPSHOT_NEW)) { // Create a new snapshot named with a unique string String unique = new BigInteger(281, new Random()).toString(13); mCurrentSaveName = "snapshotTemp-" + unique; // Create the new snapshot // ... } } }
Gespeicherte Spiele schreiben
So speichern Sie Inhalte in einem gespeicherten Spiel:
Öffnen Sie einen Snapshot asynchron mit
SnapshotsClient.open().Rufen Sie das
Snapshot-Objekt aus dem Ergebnis der Aufgabe ab, indem SieSnapshotsClient.DataOrConflict.getData()aufrufen.Rufen Sie eine
SnapshotContentsInstanz mitSnapshotsClient.SnapshotConflictab.Rufen Sie
SnapshotContents.writeBytes()auf, um die Daten des Spielers im Byteformat zu speichern.Sobald alle Änderungen geschrieben wurden, rufen Sie
SnapshotsClient.commitAndClose()auf, um die Änderungen an die Google-Server zu senden. Beim Methodenaufruf kann Ihr Spiel optional zusätzliche Informationen angeben, um den Play Games-Diensten mitzuteilen, wie dieses gespeicherte Spiel Spielern präsentiert werden soll. Diese Informationen werden in einemSnapshotMetaDataChangeObjekt dargestellt, das Ihr Spiel mitSnapshotMetadataChange.Buildererstellt.
Das folgende Snippet zeigt, wie Ihr Spiel Änderungen an einem gespeicherten Spiel übernehmen kann:
private Task<SnapshotMetadata> writeSnapshot(Snapshot snapshot, byte[] data, Bitmap coverImage, String desc) { // Set the data payload for the snapshot snapshot.getSnapshotContents().writeBytes(data); // Create the change operation SnapshotMetadataChange metadataChange = new SnapshotMetadataChange.Builder() .setCoverImage(coverImage) .setDescription(desc) .build(); SnapshotsClient snapshotsClient = PlayGames.getSnapshotsClient(this); // Commit the operation return snapshotsClient.commitAndClose(snapshot, metadataChange); }
Wenn das Gerät des Spielers nicht mit einem Netzwerk verbunden ist, wenn Ihre App
SnapshotsClient.commitAndClose() aufruft,
speichern die Play Games-Dienste die Daten des gespeicherten Spiels lokal auf dem Gerät. Sobald das Gerät wieder verbunden ist, synchronisieren die Play Games-Dienste die lokal im Cache gespeicherten Änderungen des gespeicherten Spiels mit den Google-Servern.
Gespeicherte Spiele laden
So rufen Sie gespeicherte Spiele für den authentifizierten Spieler ab:
Öffnen Sie einen Snapshot asynchron mit
SnapshotsClient.open().Rufen Sie das
SnapshotObjekt aus dem Ergebnis der Aufgabe ab, indem SieSnapshotsClient.DataOrConflict.getData()aufrufen. Alternativ kann Ihr Spiel auch einen bestimmten Snapshot über die Benutzeroberfläche für die Auswahl gespeicherter Spiele abrufen, wie unter Gespeicherte Spiele anzeigen beschrieben.Rufen Sie die
SnapshotContentsInstanz mitSnapshotsClient.SnapshotConflictab.Rufen Sie
SnapshotContents.readFully()auf, um den Inhalt des Snapshots zu lesen.
Das folgende Snippet zeigt, wie Sie ein bestimmtes gespeichertes Spiel laden können:
Task<byte[]> loadSnapshot() { // Display a progress dialog // ... // Get the SnapshotsClient from the signed in account. SnapshotsClient snapshotsClient = PlayGames.getSnapshotsClient(this); // In the case of a conflict, the most recently modified version of this snapshot will be used. int conflictResolutionPolicy = SnapshotsClient.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED; // Open the saved game using its name. return snapshotsClient.open(mCurrentSaveName, true, conflictResolutionPolicy) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Log.e(TAG, "Error while opening Snapshot.", e); } }).continueWith(new Continuation<SnapshotsClient.DataOrConflict<Snapshot>, byte[]>() { @Override public byte[] then(@NonNull Task<SnapshotsClient.DataOrConflict<Snapshot>> task) throws Exception { Snapshot snapshot = task.getResult().getData(); // Opening the snapshot was a success and any conflicts have been resolved. try { // Extract the raw data from the snapshot. return snapshot.getSnapshotContents().readFully(); } catch (IOException e) { Log.e(TAG, "Error while reading Snapshot.", e); } return null; } }).addOnCompleteListener(new OnCompleteListener<byte[]>() { @Override public void onComplete(@NonNull Task<byte[]> task) { // Dismiss progress dialog and reflect the changes in the UI when complete. // ... } }); }
Konflikte bei gespeicherten Spielen verarbeiten
Wenn Sie die Snapshots API in Ihrem Spiel verwenden, können mehrere Geräte Lese- und Schreibvorgänge für dasselbe gespeicherte Spiel ausführen. Wenn ein Gerät vorübergehend die Netzwerkverbindung verliert und später wieder eine Verbindung herstellt, kann dies zu Datenkonflikten führen, bei denen das auf dem lokalen Gerät eines Spielers gespeicherte Spiel nicht mit der Remoteversion auf den Google-Servern synchronisiert ist.
Die Snapshots API bietet einen Mechanismus zur Konfliktlösung, der beide Sätze von in Konflikt stehenden gespeicherten Spielen zur Lesezeit präsentiert und Ihnen ermöglicht, eine für Ihr Spiel geeignete Lösungsstrategie zu implementieren.
Wenn die Play Games-Dienste einen Datenkonflikt erkennen, gibt die
SnapshotsClient.DataOrConflict.isConflict()
Methode den Wert true zurück. In diesem Fall bietet die
SnapshotsClient.SnapshotConflict
Klasse zwei Versionen des gespeicherten Spiels:
Serverversion: Die aktuellste Version, die von den Play Games-Diensten als korrekt für das Gerät des Spielers angesehen wird.
Lokale Version: Eine geänderte Version, die auf einem der Geräte des Spielers erkannt wurde und in Konflikt stehende Inhalte oder Metadaten enthält. Diese Version ist möglicherweise nicht identisch mit der Version, die Sie speichern wollten.
Ihr Spiel muss entscheiden, wie der Konflikt gelöst werden soll, indem eine der bereitgestellten Versionen ausgewählt oder die Daten der beiden Versionen des gespeicherten Spiels zusammengeführt werden.
So erkennen und lösen Sie Konflikte bei gespeicherten Spielen:
Rufen Sie
SnapshotsClient.open()auf. Das Ergebnis der Aufgabe enthält eineSnapshotsClient.DataOrConflict-Klasse.Rufen Sie die
SnapshotsClient.DataOrConflict.isConflict()Methode auf. Wenn das Ergebnis „true“ ist, müssen Sie einen Konflikt lösen.Rufen Sie
SnapshotsClient.DataOrConflict.getConflict()auf, um eineSnapshotsClient.snapshotConflict-Instanz abzurufen.Rufen Sie
SnapshotsClient.SnapshotConflict.getConflictId()auf, um die Konflikt-ID abzurufen, die den erkannten Konflikt eindeutig identifiziert. Ihr Spiel benötigt diesen Wert, um später eine Anfrage zur Konfliktlösung zu senden.Rufen Sie
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()auf, um die lokale Version abzurufen.Rufen Sie
SnapshotsClient.SnapshotConflict.getSnapshot()auf, um die Serverversion abzurufen.Um den Konflikt bei gespeicherten Spielen zu lösen, wählen Sie eine Version aus, die Sie als endgültige Version auf dem Server speichern möchten und übergeben Sie sie an die
SnapshotsClient.resolveConflict()Methode.
Das folgende Snippet zeigt ein Beispiel dafür, wie Ihr Spiel einen Konflikt bei gespeicherten Spielen verarbeiten kann, indem es das zuletzt geänderte gespeicherte Spiel als endgültige Version zum Speichern auswählt:
private static final int MAX_SNAPSHOT_RESOLVE_RETRIES = 10; Task<Snapshot> processSnapshotOpenResult(SnapshotsClient.DataOrConflict<Snapshot> result, final int retryCount) { if (!result.isConflict()) { // There was no conflict, so return the result of the source. TaskCompletionSource<Snapshot> source = new TaskCompletionSource<>(); source.setResult(result.getData()); return source.getTask(); } // There was a conflict. Try resolving it by selecting the newest of the conflicting snapshots. // This is the same as using RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED as a conflict resolution // policy, but we are implementing it as an example of a manual resolution. // One option is to present a UI to the user to choose which snapshot to resolve. SnapshotsClient.SnapshotConflict conflict = result.getConflict(); Snapshot snapshot = conflict.getSnapshot(); Snapshot conflictSnapshot = conflict.getConflictingSnapshot(); // Resolve between conflicts by selecting the newest of the conflicting snapshots. Snapshot resolvedSnapshot = snapshot; if (snapshot.getMetadata().getLastModifiedTimestamp() < conflictSnapshot.getMetadata().getLastModifiedTimestamp()) { resolvedSnapshot = conflictSnapshot; } return PlayGames.getSnapshotsClient(theActivity) .resolveConflict(conflict.getConflictId(), resolvedSnapshot) .continueWithTask( new Continuation< SnapshotsClient.DataOrConflict<Snapshot>, Task<Snapshot>>() { @Override public Task<Snapshot> then( @NonNull Task<SnapshotsClient.DataOrConflict<Snapshot>> task) throws Exception { // Resolving the conflict may cause another conflict, // so recurse and try another resolution. if (retryCount < MAX_SNAPSHOT_RESOLVE_RETRIES) { return processSnapshotOpenResult(task.getResult(), retryCount + 1); } else { throw new Exception("Could not resolve snapshot conflicts"); } } }); }
Gespeicherte Spiele ändern
Wenn Sie Daten aus mehreren gespeicherten Spielen zusammenführen oder einen vorhandenen Snapshot ändern möchten, um ihn als aufgelöste endgültige Version auf dem Server zu speichern, gehen Sie so vor:
Rufen Sie
SnapshotsClient.open()auf.Rufen Sie
SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent()auf, um ein neuesSnapshotContents-Objekt abzurufen.Führen Sie die Daten aus
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()undSnapshotsClient.SnapshotConflict.getSnapshot()in dasSnapshotContents-Objekt aus dem vorherigen Schritt zusammen.Erstellen Sie optional eine
SnapshotMetadataChange-Instanz, wenn es Änderungen an den Metadatenfeldern gibt.Rufen Sie
SnapshotsClient.resolveConflict()auf. Übergeben Sie beim MethodenaufrufSnapshotsClient.SnapshotConflict.getConflictId()als erstes Argument und die zuvor geänderten ObjekteSnapshotMetadataChangeundSnapshotContentsals zweites bzw. drittes Argument .Wenn der
SnapshotsClient.resolveConflict()Aufruf erfolgreich ist, speichert die API dasSnapshotObjekt auf dem Server und versucht, das Snapshot-Objekt auf Ihrem lokalen Gerät zu öffnen.- Wenn ein Konflikt vorliegt,
SnapshotsClient.DataOrConflict.isConflict()gibttruezurück. In diesem Fall sollte Ihr Spiel zu Schritt 2 zurückkehren und die Schritte zum Ändern des Snapshots wiederholen, bis die Konflikte gelöst sind. - Wenn kein Konflikt vorliegt,
SnapshotsClient.DataOrConflict.isConflict()gibtfalsezurück und dasSnapshotObjekt kann von Ihrem Spiel geändert werden.
- Wenn ein Konflikt vorliegt,