במדריך הזה מוסבר איך להטמיע משחקים שמורים באמצעות
snapshots API שמסופק על ידי Google Play Games Services. תוכלו למצוא את ממשקי ה-API
com.google.android.gms.games.snapshot
וגם
com.google.android.gms.games
חבילות.
לפני שמתחילים
כדי לקבל מידע על התכונה, אפשר לעיין במאמר בנושא סקירה כללית של משחקים שמורים.
- הפעלת התמיכה במשחקים שמורים בשביל המשחק ב-Google Play Console.
- אפשר להוריד ולבדוק את דוגמת הקוד של המשחקים השמורים ב דף דוגמאות ל-Android
- מומלץ להכיר את ההמלצות שמתוארות במאמר רשימת משימות איכות.
קבלת קובץ ה-snapshot
כדי להתחיל להשתמש ב-snapshot API, המשחק שלך צריך קודם לקבל
אובייקט SnapshotsClient
. אפשר לעשות זאת באמצעות קריאה ל
Games.getSnapshotsClient()
ומעבירים את
פעילות.
ציון ההיקף של האחסון
ה-Snaps API מסתמך על Google Drive API
לאחסון משחקים שמורים. כדי לגשת ל-Drive API, האפליקציה שלך צריכה לציין את
Drive.SCOPE_APPFOLDER
את היקף ההרשאות בזמן היצירה של לקוח הכניסה באמצעות חשבון Google.
לפניכם דוגמה לאופן שבו עושים זאת
onResume()
עבור פעילות הכניסה שלך:
@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 } } }); }
הצגת המשחקים השמורים
אפשר לשלב את ה-API של קובצי snapshot בכל מקום שבו המשחק מספק לשחקנים אפשרות לשמור או לשחזר את ההתקדמות שלהם. המשחק שלך עשוי להציג בנקודות שמירה/שחזור ייעודיות, או מתן אפשרות לשחקנים לשמור או לשחזר בכל שלב.
אחרי שהשחקנים בוחרים באפשרות השמירה או השחזור במשחק, הם יכולים אפשר גם להציג מסך שיבקש מהשחקנים להזין מידע לקובץ חדש שנשמר משחק או לבחור משחק שמור קיים לשחזור.
כדי לפשט את תהליך הפיתוח, ה-snapshot API מספק משתמש ברירת מחדל לבחירת משחקים שמורים ממשק (UI) שאפשר להשתמש בו מחוץ לאריזה. ממשק המשתמש לבחירת משחקים שמורים מאפשר לשחקנים ליצור משחק שמור חדש, להציג פרטים על משחקים שמורים קיימים ולטעון משחקים קודמים שנשמרו.
כדי להפעיל את ממשק המשתמש של המשחקים השמורים שמוגדר כברירת מחדל:
- התקשרו למספר
SnapshotsClient.getSelectSnapshotIntent()
כדי לקבלIntent
להפעלת ממשק המשתמש לבחירת משחקים שנשמרו כברירת מחדל. - שיחת טלפון
startActivityForResult()
ונעביר את זהIntent
אם הקריאה תסתיים בהצלחה, המשחק יציג את ממשק המשתמש לבחירת משחק שמור, יחד עם האפשרויות שציינת.
הדוגמה הבאה מראה איך להפעיל את ממשק המשתמש לבחירת משחקים שמורים שמוגדר כברירת מחדל:
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); } }); }
אם השחקן בוחר ליצור משחק שמור חדש או לטעון משחק שמור קיים,
ממשק המשתמש שולח בקשה ל-Play Games Services. אם הבקשה מצליחה,
שירות Play Games Services מחזיר מידע כדי ליצור או לשחזר את המשחק השמור באמצעות
onActivityResult()
קריאה חוזרת. המשחק שלך יכול לבטל את הקריאה החוזרת (callback) הזו כדי לבדוק אם התרחשו שגיאות במהלך הבקשה.
קטע הקוד הבא מציג הטמעה לדוגמה של
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 // ... } } }
כתיבת משחקים שמורים
כדי לאחסן תוכן במשחק שמור:
פתיחה אסינכרונית של קובץ snapshot דרך
SnapshotsClient.open()
מאחזרים את אובייקט
Snapshot
מתוצאת המשימה באמצעות קריאה לאובייקטSnapshotsClient.DataOrConflict.getData()
אחזור
SnapshotContents
מופע עםSnapshotsClient.SnapshotConflict
שיחת טלפון
SnapshotContents.writeBytes()
כדי לאחסן את נתוני הנגן בפורמט בייטים.אחרי שכל השינויים נכתבים, קוראים
SnapshotsClient.commitAndClose()
כדי לשלוח את השינויים שלכם לשרתים של Google. בשיחת ה-method, המשחק יכול אפשר לספק מידע נוסף כדי להנחות את Play Games Services איך הצגת המשחק השמור הזה לשחקנים. מידע זה מיוצג באמצעותSnapshotMetaDataChange
אובייקט, שהמשחק יוצר באמצעותSnapshotMetadataChange.Builder
קטע הקוד הבא מראה איך המשחק עשוי לבצע שינויים במשחק שמור:
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); }
אם המכשיר של הנגן לא מחובר לרשת כשהאפליקציה מתקשרת
SnapshotsClient.commitAndClose()
אפליקציית Play Games Services שומרת את נתוני המשחקים שנשמרו באופן מקומי במכשיר. במכשיר
להתחבר מחדש, Play Games Services מסנכרנת את השינויים במשחקים שנשמרו במטמון המקומי
השרתים של Google.
טעינת המשחקים השמורים
כדי לאחזר משחקים שנשמרו עבור השחקן שמחובר כרגע:
פתיחה אסינכרונית של קובץ snapshot באמצעות
SnapshotsClient.open()
הוצאה
Snapshot
מתוצאת המשימה באמצעות קריאהSnapshotsClient.DataOrConflict.getData()
לחלופין, המשחק שלך יכול גם לאחזר נתונים ספציפיים של תמונת מצב דרך ממשק המשתמש לבחירת משחקים שמורים, כפי שמתואר הצגת משחקים שמורים.מאחזרים את
SnapshotContents
מופע עםSnapshotsClient.SnapshotConflict
שיחת טלפון
SnapshotContents.readFully()
כדי לקרוא את התוכן של קובץ ה-snapshot.
קטע הקוד הבא מראה איך אפשר לטעון משחק שמור ספציפי:
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. // ... } }); }
טיפול בהתנגשויות במשחקים שמורים
כשמשתמשים ב-snapshot API במשחק, אפשר ליצור מספר פיצ'רים מכשירים לביצוע קריאה וכתיבה באותו משחק שמור. במקרה שבו החיבור לרשת של המכשיר מתנתק באופן זמני ומתחבר מחדש מאוחר יותר, המצב הזה עלול לגרום לגרום להתנגשויות נתונים שבהן המשחק השמור מאוחסן במכשיר המקומי של השחקן לא מסונכרן עם הגרסה המרוחקת שמאוחסנת בשרתי Google.
ה-snapshot API מספק מנגנון לפתרון התנגשויות קבוצות של משחקים שמורים בעלי מאפיינים זהים לחשבון פעיל בזמן הקריאה ומאפשרים פתרון שמתאימה למשחק שלכם.
כש-Play Games Services מזהה התנגשות נתונים,
SnapshotsClient.DataOrConflict.isConflict()
ה-method מחזירה את הערך true
באירוע הזה, הפונקציה
SnapshotsClient.SnapshotConflict
class מספק שתי גרסאות של המשחק השמור:
גרסת שרת: הגרסה העדכנית ביותר שידועה ל-Play Games Services ל להיות מדויקות בהתאם למכשיר של הנגן.
גרסה מקומית: גרסה מקומית שזוהתה באחד מהמכשירים של הנגן שמכיל תוכן או מטא-נתונים מתנגשים. זה לא יכול להיות זהה הגרסה שניסית לשמור.
המשחק שלך צריך להחליט איך לפתור את המחלוקת על ידי בחירה באחת מהאפשרויות שסופקו או מיזוג הנתונים של שתי הגרסאות השמורות של המשחקים.
כדי לזהות ולפתור התנגשויות בין משחקים שמורים:
שיחת טלפון
SnapshotsClient.open()
תוצאת המשימה מכילה מחלקהSnapshotsClient.DataOrConflict
.קוראים לפונקציה
SnapshotsClient.DataOrConflict.isConflict()
. אם התוצאה היא נכונה, יש בעיה שצריך לפתור.שיחת טלפון
SnapshotsClient.DataOrConflict.getConflict()
כדי לאחזרSnaphotsClient.snapshotConflict
מכונה.שיחת טלפון
SnapshotsClient.SnapshotConflict.getConflictId()
כדי לאחזר את המזהה של ההתנגשות שמזהה באופן ייחודי את ההתנגשות שזוהה. שלך המשחק צריך את הערך הזה כדי לשלוח בקשה לפתרון התנגשויות מאוחר יותר.שיחת טלפון
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
כדי לקבל את הגרסה המקומית.שיחת טלפון
SnapshotsClient.SnapshotConflict.getSnapshot()
כדי לקבל את גרסת השרת.כדי לפתור את ההתנגשות במשחק שנשמר, צריך לבחור גרסה שבה רוצים לשמור את השרת כגרסה הסופית ומעבירים אותו
SnapshotsClient.resolveConflict()
.
קטע הקוד הבא מציג ודוגמה לאופן שבו המשחק שלך יכול להתמודד עם התנגשות במשחק שנשמר באמצעות בחירת המשחק האחרון שנשמר כגרסה הסופית לשמירה:
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"); } } }); }
שינוי משחקים שמורים
אם רוצים למזג נתונים מכמה משחקים שמורים או לשנות משחק קיים
Snapshot
כדי לשמור בשרת כגרסה הסופית שטופלה, צריך לפעול לפי השלבים הבאים
שלבים:
שיחת טלפון
SnapshotsClient.open()
שיחת טלפון
SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent()
כדי לקבל אובייקטSnapshotContents
.מיזוג הנתונים מתוך
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
וגםSnapshotsClient.SnapshotConflict.getSnapshot()
אל תוךSnapshotContents
מהשלב הקודם.לחלופין, אפשר ליצור
SnapshotMetadataChange
למשל, אם בוצעו שינויים בשדות המטא-נתונים.שיחת טלפון
SnapshotsClient.resolveConflict()
בשיחת ה-method, מעביריםSnapshotsClient.SnapshotConflict.getConflictId()
כארגומנט הראשון,SnapshotMetadataChange
וגםSnapshotContents
אובייקטים ששינית קודם לכן כאובייקטים השניים ארגומנטים של שלישי, בהתאמה.אם
SnapshotsClient.resolveConflict()
הקריאה בוצעה בהצלחה, ה-API מאחסן את האובייקטSnapshot
בשרת מנסה לפתוח את האובייקט snapshot במכשיר המקומי.- אם יש התנגשות, הפונקציה
SnapshotsClient.DataOrConflict.isConflict()
מחזירהtrue
. כאן המשחק צריך לחזור לשלב 2 ולחזור על השלבים כדי לשנות את תמונת המצב עד התנגשויות נפתרו. - אם אין מחלוקת,
SnapshotsClient.DataOrConflict.isConflict()
מחזירה את הערךfalse
והאובייקטSnapshot
פתוח כדי שהמשחק יוכל לשנות אותו.
- אם יש התנגשות, הפונקציה