Ce guide vous explique comment implémenter un jeu enregistré à l'aide du
Instantanés fournies par les services de jeux Google Play. Les API sont disponibles dans les packages com.google.android.gms.games.snapshot
et com.google.android.gms.games
.
Avant de commencer
Si vous ne l'avez pas encore fait, il peut être utile de consulter les Concepts des jeux enregistrés
- Veillez à activer la compatibilité avec les jeux enregistrés. pour votre jeu dans la Google Play Console.
- Téléchargez et examinez l'exemple de code de jeu enregistré sur la page des exemples Android.
- Familiarisez-vous avec les recommandations décrites dans la checklist de contrôle qualité.
Obtenir le client d'instantanés
Pour commencer à utiliser l'API Snapshots, votre jeu doit d'abord se procurer un objet SnapshotsClient
. Pour ce faire, appelez la méthode
Games.getSnapshotsClient()
et en transmettant
et l'activité GoogleSignInAccount
du joueur actif. Pour savoir comment
récupérer les informations du compte du joueur,
Connectez-vous aux jeux Android.
Spécifier le champ d'application Drive
L'API Snapshots s'appuie sur l'API Google Drive pour stocker les jeux enregistrés. À
à l'API Drive, votre application doit spécifier
Drive.SCOPE_APPFOLDER
lors de la création du client Google Sign-In.
L'exemple suivant contient la procédure à suivre dans la méthode onResume()
pour votre activité de connexion :
private GoogleSignInClient mGoogleSignInClient; @Override protected void onResume() { super.onResume(); signInSilently(); } private void signInSilently() { GoogleSignInOptions signInOption = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN) // Add the APPFOLDER scope for Snapshot support. .requestScopes(Drive.SCOPE_APPFOLDER) .build(); GoogleSignInClient signInClient = GoogleSignIn.getClient(this, signInOption); signInClient.silentSignIn().addOnCompleteListener(this, new OnCompleteListener<GoogleSignInAccount>() { @Override public void onComplete(@NonNull Task<GoogleSignInAccount> task) { if (task.isSuccessful()) { onConnected(task.getResult()); } else { // Player will need to sign-in explicitly using via UI } } }); }
Affichage des jeux enregistrés
Vous pouvez intégrer l'API Snapshots partout où votre jeu offre aux joueurs la possibilité d'enregistrer ou de restaurer leur progression. Votre jeu peut afficher une option de ce type à certains points d'enregistrement/de restauration, ou permettre aux joueurs d'enregistrer ou de restaurer leur progression à tout moment.
Une fois l'option d'enregistrement ou de restauration sélectionnée, le jeu peut également afficher un écran qui invite le joueur à saisir les informations d'un nouveau jeu enregistré ou à sélectionner un jeu existant à restaurer.
Pour simplifier le développement, l'API Snapshots fournit une interface utilisateur (UI) de sélection de jeux enregistrés par défaut prête à l'emploi L'UI de sélection de jeux enregistrés permet aux joueurs de créer un jeu enregistré, de consulter les détails des jeux enregistrés existants et de charger de précédents jeux enregistrés.
Pour lancer l'UI Jeux enregistrés par défaut :
- Appelez
SnapshotsClient.getSelectSnapshotIntent()
pour obtenir uneIntent
pour lancer l'application par défaut UI de sélection de jeux enregistrés - Appeler
startActivityForResult()
et transmettez ceIntent
. Si l'appel aboutit, le jeu affiche l'UI de sélection de jeux enregistrés, ainsi que les options que vous avez spécifiées.
Voici un exemple de lancement de l'UI de sélection de jeux enregistrés par défaut :
private static final int RC_SAVED_GAMES = 9009; private void showSavedGamesUI() { SnapshotsClient snapshotsClient = Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(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); } }); }
Si le joueur choisit de créer un jeu enregistré ou de charger un jeu enregistré existant :
l'UI envoie une requête
aux services de jeux Google Play. Si la requête aboutit,
Les services de jeux Google Play renvoient des informations pour créer ou restaurer le jeu enregistré via
le onActivityResult()
. Votre jeu peut ignorer ce rappel pour vérifier si des erreurs se sont produites lors de la requête.
L'extrait de code suivant fournit un exemple d'implémentation de 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 // ... } } }
Écrire des jeux enregistrés
Pour stocker du contenu dans un jeu enregistré :
- Ouvrez un instantané de manière asynchrone via
SnapshotsClient.open()
. Ensuite, récupérez l'objetSnapshot
. à partir du résultat de la tâche en appelantSnapshotsClient.DataOrConflict.getData()
. - Récupérez une instance
SnapshotContents
viaSnapshotsClient.SnapshotConflict
. - Appelez
SnapshotContents.writeBytes()
pour stocker les données du joueur au format octet. - Une fois toutes vos modifications écrites, appelez
SnapshotsClient.commitAndClose()
pour envoyer vos modifications aux serveurs de Google. Dans l'appel de méthode, votre jeu peut éventuellement fournir des informations supplémentaires pour indiquer aux services de jeux Google Play comment présenter ce jeu enregistré aux joueurs. Ces informations sont représentées dans un objetSnapshotMetaDataChange
, que votre jeu crée à l'aide deSnapshotMetadataChange.Builder
.
L'extrait de code suivant montre comment votre jeu peut apporter des modifications à un jeu enregistré :
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 = Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(this)); // Commit the operation return snapshotsClient.commitAndClose(snapshot, metadataChange); }
Si l'appareil du joueur n'est pas connecté à un réseau lorsque votre application appelle
SnapshotsClient.commitAndClose()
, les services de jeux Google Play stockent les données de jeu enregistrées localement sur
l'appareil. Lors de la reconnexion de l'appareil, les services de jeux Google Play synchronisent le jeu enregistré mis en cache localement.
sur les serveurs de Google.
Chargement des jeux enregistrés...
Pour récupérer les jeux enregistrés du joueur actuellement connecté :
- Ouvrez un instantané de manière asynchrone via
SnapshotsClient.open()
. Ensuite, récupérez l'objetSnapshot
. à partir du résultat de la tâche en appelantSnapshotsClient.DataOrConflict.getData()
. Sinon, votre jeu peut également récupérer un instantané spécifique via l'interface utilisateur de sélection des jeux enregistrés, comme décrit dans Afficher les jeux enregistrés - Récupérez l'instance
SnapshotContents
viaSnapshotsClient.SnapshotConflict
. - Appelez
SnapshotContents.readFully()
pour lire le contenu de l'instantané.
L'extrait de code suivant montre comment charger un jeu enregistré spécifique :
Task<byte[]> loadSnapshot() { // Display a progress dialog // ... // Get the SnapshotsClient from the signed in account. SnapshotsClient snapshotsClient = Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(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. // ... } }); }
Gérer les conflits liés aux jeux enregistrés
Lorsque vous utilisez l'API Snapshots dans votre jeu, il peut arriver que plusieurs appareils exécutent des opérations de lecture et d'écriture dans le même jeu enregistré. En cas de perte de connexion temporaire, puis de reconnexion d'un appareil, des conflits de données peuvent se produire, car le jeu enregistré stocké sur l'appareil local du joueur n'est plus synchronisé avec la version distante stockée sur les serveurs de Google.
L'API Snapshots fournit un mécanisme de résolution des conflits qui présente les deux jeux enregistrés en conflit au moment de la lecture et vous permet d'implémenter une stratégie de résolution adaptée à votre jeu.
Lorsque les services de jeux Google Play détectent un conflit de données,
La méthode SnapshotsClient.DataOrConflict.isConflict()
renvoie la valeur true
. Dans cet événement, la méthode
La classe SnapshotsClient.SnapshotConflict
fournit deux versions du jeu enregistré:
- Version du serveur: version la plus récente connue des services de jeux Google Play pour l'appareil du joueur. et
- Version locale: version modifiée détectée sur l'un des appareils du lecteur et contenant de contenus ou de métadonnées conflictuels. Cette version peut être différente de celle que vous essayez d'enregistrer.
Votre jeu doit déterminer comment résoudre le conflit en choisissant l'une des versions fournies ou en fusionnant les données des deux versions enregistrées.
Pour détecter et résoudre les conflits liés aux jeux enregistrés :
- Appelez
SnapshotsClient.open()
. Le résultat de la tâche contient une classeSnapshotsClient.DataOrConflict
. - Appelez la méthode
SnapshotsClient.DataOrConflict.isConflict()
. Si le résultat est "true", conflit à résoudre. - Appelez
SnapshotsClient.DataOrConflict.getConflict()
pour récupérer un InstanceSnaphotsClient.snapshotConflict
. - Appelez
SnapshotsClient.SnapshotConflict.getConflictId()
pour récupérer l'ID de conflit unique identifie le conflit détecté. Votre jeu a besoin de cette valeur pour envoyer une demande de résolution de conflit plus tard. - Appelez
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
pour obtenir la version locale. - Appelez
SnapshotsClient.SnapshotConflict.getSnapshot()
pour obtenir la version du serveur. - Pour résoudre le conflit, sélectionnez une version que vous souhaitez enregistrer sur le serveur en tant que
la version finale, puis la transmettre à la méthode
SnapshotsClient.resolveConflict()
.
L'extrait de code suivant montre comment votre jeu peut gérer un conflit entre jeux enregistrés en sélectionnant celui dont les modifications sont les plus récentes comme version finale à enregistrer :
private static final int MAX_SNAPSHOT_RESOLVE_RETRIES = 10; TaskS<napshot >processSnapshotOpenResult(SnapshotsClient.DataOrConflictS<napshot >result, final int retryCount) { if (!result.isConflict()) { // There was no conflict, so return the result of the source. TaskCompletionSourceS<napshot >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 Games.getSnapshotsClient(theActivity, GoogleSignIn.getLastSignedInAccount(this)) .resolveConflict(conflict.getConflictId(), resolvedSnapshot) .continueWithTask( new Continuation < SnapshotsClient.DataOrConflictS<napshot,> TaskS<napshot(>>) { @Override public TaskS<napshot >then( @NonNull TaskS<napshotsClient.DataOrConflictS<napshot >>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(C"ould not resolve snapshot conflicts)"; } } }); }
Modifier les jeux enregistrés pour résoudre les conflits
Si vous souhaitez fusionner les données de plusieurs jeux enregistrés ou modifier un Snapshot
existant
pour enregistrer sur le serveur en tant que version finale résolue, procédez comme suit:
- Appelez
SnapshotsClient.open()
. - Appelez
SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent()
pour obtenir un nouveau objetSnapshotContents
. - Fusionner les données de
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
etSnapshotsClient.SnapshotConflict.getSnapshot()
dans l'objetSnapshotContents
à partir de à l'étape précédente. - Vous pouvez également créer une instance
SnapshotMetadataChange
en cas de modification des métadonnées. . - Appelez
SnapshotsClient.resolveConflict()
. Dans votre appel de méthode, transmettezSnapshotsClient.SnapshotConflict.getConflictId()
comme premier argument, et l'argument les objetsSnapshotMetadataChange
etSnapshotContents
que vous avez modifiés précédemment en tant que deuxième et troisièmes arguments respectivement. - Si l'appel
SnapshotsClient.resolveConflict()
aboutit, l'API stockeSnapshot
au serveur et tente d'ouvrir l'objet Snapshot sur votre appareil local.- En cas de conflit,
SnapshotsClient.DataOrConflict.isConflict()
renvoietrue
. Dans ce cas, votre jeu doit revenir à l'étape 2 et répéter les étapes de modification de l'instantané jusqu'à ce que les conflits soient résolus. - En l'absence de conflit,
SnapshotsClient.DataOrConflict.isConflict()
renvoiefalse
et l'objetSnapshot
est ouvert pour que votre jeu puisse le modifier.
- En cas de conflit,