En esta guía, se muestra cómo implementar la función de juegos guardados con la
La API de snapshots que proporcionan los Servicios de juego de Google Play. Se pueden encontrar las APIs en los paquetes com.google.android.gms.games.snapshot
y com.google.android.gms.games
.
Antes de comenzar
Si aún no lo has hecho, puede resultarte útil consultar el Conceptos de Juegos guardados
- Asegúrate de habilitar la compatibilidad con los juegos guardados. para tu juego en Google Play Console.
- Descarga y revisa la muestra de código de los juegos guardados en la página de muestras de Android.
- Familiarízate con las recomendaciones que se describen en la lista de tareas de calidad.
Obtén el cliente de instantáneas
Para comenzar a usar la API de snapshots, el juego primero debe obtener un objeto SnapshotsClient
. Puedes hacerlo llamando al
Games.getSnapshotsClient()
y pasa el
y el GoogleSignInAccount
del jugador actual. Para aprender a hacer lo siguiente:
recuperar la información de la cuenta del jugador, consulta
Acceso a juegos para Android.
Cómo especificar el alcance de Drive
La API de snapshots se basa en la API de Google Drive para el almacenamiento de juegos guardados. Para
acceder a la API de Drive, tu aplicación debe especificar la
Drive.SCOPE_APPFOLDER
cuando se compila el cliente de Acceso con Google.
Este es un ejemplo de cómo hacerlo en el método onResume()
para tu actividad de acceso:
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 } } }); }
Cómo mostrar los juegos guardados
Puedes integrar la API de snapshots en cualquier lugar que tu juego les brinde a los jugadores la opción de guardar o restablecer su progreso. Tu juego puede mostrar esta opción en los puntos designados de guardar/restablecer o permitir que los jugadores guarden o restablezcan el progreso en cualquier momento.
Una vez que los jugadores seleccionen la opción de guardar/restablecer el juego, este podrá mostrar, de manera opcional, una pantalla que les pedirá que introduzcan la información para un nuevo juego guardado o que seleccionen uno existente a fin de restablecerlo.
A fin de simplificar el desarrollo, la API de snapshots proporciona una interfaz de usuario (IU) de selección de juegos guardados predeterminada que está lista para usar. La IU selección de juegos guardados permite que los jugadores creen un nuevo juego guardado, consulten los detalles de los juegos guardados existentes y carguen los juegos guardados anteriores.
Para iniciar la IU predeterminada de juegos guardados, haz lo siguiente:
- Llama a
SnapshotsClient.getSelectSnapshotIntent()
para obtener unaIntent
para iniciar la configuración IU de selección de juegos guardados. - Llama a
startActivityForResult()
y pasa eseIntent
. Si la llamada se realiza de forma correcta, el juego muestra la IU de selección de juegos guardados, junto con las opciones que especificaste.
Este es un ejemplo de cómo iniciar la IU de selección de juegos guardados predeterminada:
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 el jugador selecciona crear un nuevo juego guardado o cargar uno existente,
la IU envía una solicitud a los Servicios de juego de Google Play. Si la solicitud tiene éxito,
Los Servicios de juego de Google Play devuelven información para crear o restablecer el juego guardado mediante
el onActivityResult()
devolución de llamada. Tu juego puede anular esta devolución de llamada para comprobar si se produjeron errores durante la solicitud.
El siguiente fragmento de código muestra una implementación de muestra 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 // ... } } }
Cómo escribir juegos guardados
Para almacenar contenido en un juego guardado, haz lo siguiente:
- Abre una instantánea de forma asíncrona a través de
SnapshotsClient.open()
. Luego, recupera el objetoSnapshot
. a partir del resultado de la tarea llamando aSnapshotsClient.DataOrConflict.getData()
. - Recupera una instancia de
SnapshotContents
a través deSnapshotsClient.SnapshotConflict
. - Llama a
SnapshotContents.writeBytes()
para almacenar los datos del reproductor en formato de bytes. - Una vez que hayas escrito todos los cambios, llama
SnapshotsClient.commitAndClose()
para enviar los cambios a los servidores de Google. En la llamada de método, tu juego puede proporcionar información adicional para indicarles a los Servicios de juego de Google Play cómo presentar este juego guardado a los jugadores. Esta información se representa en unSnapshotMetaDataChange
. que el juego crea conSnapshotMetadataChange.Builder
.
En el siguiente fragmento, se muestra cómo tu juego puede confirmar los cambios en un juego guardado:
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 el dispositivo del jugador no está conectado a una red cuando llama la app
SnapshotsClient.commitAndClose()
, los Servicios de juego de Google Play almacenan los datos del juego guardado de forma local en
el dispositivo. Cuando se vuelve a conectar el dispositivo, los Servicios de juego de Google Play sincronizan el juego que se guardó en la caché local
en los servidores de Google.
Cargando juegos guardados
Si deseas recuperar los juegos guardados para el jugador actualmente conectado, haz lo siguiente:
- Abre una instantánea de forma asíncrona a través de
SnapshotsClient.open()
. Luego, recupera el objetoSnapshot
. a partir del resultado de la tarea llamando aSnapshotsClient.DataOrConflict.getData()
. Como alternativa, El juego también puede recuperar un resumen específico a través de la IU de selección de juegos guardados, como se describe en Cómo mostrar los juegos guardados - Recupera la instancia
SnapshotContents
a través deSnapshotsClient.SnapshotConflict
. - Llama a
SnapshotContents.readFully()
para leer el contenido de la instantánea.
En el siguiente fragmento, se muestra cómo podrías cargar un juego guardado específico:
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. // ... } }); }
Manejo de conflictos de juegos guardados
Cuando se utiliza la API de snapshots en el juego, es posible que varios dispositivos realicen operaciones de lectura y escritura en el mismo juego guardado. En el caso de que un dispositivo pierda, de forma temporal, su conexión de red y se vuelva a conectar más tarde, se podrían producir conflictos de datos por los que el juego guardado que se almacenó en el dispositivo local del jugador no se sincronice con la versión remota que se almacenó en los servidores de Google.
La API de snapshots proporciona un mecanismo de resolución de conflictos que presenta ambos conjuntos de juegos guardados en conflicto en el momento de la lectura y te permite implementar una estrategia de resolución adecuada para tu juego.
Cuando los Servicios de juego de Google Play detectan un conflicto de datos, el
El método SnapshotsClient.DataOrConflict.isConflict()
devuelve un valor true
. En este evento, el
La clase SnapshotsClient.SnapshotConflict
proporciona dos versiones del juego guardado:
- Versión del servidor: Es la versión más actualizada que conocen los Servicios de juego de Google Play para que sea precisa. para el dispositivo del jugador y
- Versión local: Una versión modificada detectada en uno de los dispositivos del jugador que contiene contenido o metadatos contradictorios. Es posible que no sea la misma que la versión que intentaste guardar.
Tu juego debe decidir cómo resolver el conflicto seleccionando una de las versiones proporcionadas o fusionando los datos de las dos versiones guardadas del juego.
Para detectar y resolver conflictos de juegos guardados, haz lo siguiente:
- Llama a
SnapshotsClient.open()
. El resultado de la tarea contiene una claseSnapshotsClient.DataOrConflict
. - Llama al método
SnapshotsClient.DataOrConflict.isConflict()
. Si el resultado es verdadero, tienes un conflicto por resolver. - Llama a
SnapshotsClient.DataOrConflict.getConflict()
para recuperar unSnaphotsClient.snapshotConflict
. - Llama a
SnapshotsClient.SnapshotConflict.getConflictId()
para recuperar el ID de conflicto que identifica el conflicto detectado. Tu juego necesita este valor para enviar una solicitud de resolución de conflictos más adelante. - Llama a
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
para obtener la versión local. - Llama a
SnapshotsClient.SnapshotConflict.getSnapshot()
para obtener la versión del servidor. - Para resolver el conflicto del juego guardado, selecciona la versión que quieras guardar en el servidor como el
versión final y pasarla al método
SnapshotsClient.resolveConflict()
.
En el siguiente fragmento, se muestra un ejemplo de cómo el juego podría controlar un conflicto de juego guardado seleccionando el que se modificó recientemente como la versión final para guardar:
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)"; } } }); }
Modificación de los juegos guardados para la resolución de conflictos
Si quieres combinar datos de varios juegos guardados o modificar un Snapshot
existente
para guardar en el servidor como la versión final resuelta, sigue estos pasos:
- Llama a
SnapshotsClient.open()
. - Llama a
SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent()
para obtener uno nuevo. objetoSnapshotContents
. - Combina los datos de
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
ySnapshotsClient.SnapshotConflict.getSnapshot()
al objetoSnapshotContents
de paso anterior. - De forma opcional, crea una instancia de
SnapshotMetadataChange
si se realizan cambios en los metadatos. . - Llama a
SnapshotsClient.resolveConflict()
. En tu llamada de método, pasaSnapshotsClient.SnapshotConflict.getConflictId()
como primer argumento Los objetosSnapshotMetadataChange
ySnapshotContents
que modificaste antes como la segunda y terceros, respectivamente. - Si la llamada a
SnapshotsClient.resolveConflict()
se realiza de forma correcta, la API almacena elSnapshot
al servidor y, luego, intenta abrir el objeto Snapshot en tu dispositivo local.- Si hay un conflicto,
SnapshotsClient.DataOrConflict.isConflict()
muestratrue
. En este caso, el juego debería volver al paso 2 y repetir los pasos para modificar la instantánea hasta que se resuelvan los conflictos. - Si no hay ningún conflicto,
SnapshotsClient.DataOrConflict.isConflict()
muestrafalse
y El objetoSnapshot
está abierto para que lo modifique tu juego.
- Si hay un conflicto,