במדריך הזה מוסבר איך מטמיעים משחקי משחקים שמורים באמצעות
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()
ומעבירים את
פעילות.
ציון ההיקף של Drive
ה-snapshot 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 OnCompleteListenerG<oogleSignInAccount(>) { @Override public void onComplete(@NonNull TaskG<oogleSignInAccount >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); } }); }
אם השחקן בוחר ליצור משחק שמור חדש או לטעון משחק שמור קיים,
ממשק המשתמש שולח בקשה לשירותי המשחקים של Google Play. אם הבקשה מצליחה,
Google 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, המשחק יכול לספק מידע נוסף כדי להנחות את Google 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()
, נתוני המשחקים השמורים של Google Play Services נשמרים באופן מקומי במכשיר
במכשיר. אחרי חיבור מחדש של המכשיר, שירותי המשחקים של Google Play מסנכרנים את המשחק שנשמר במטמון המקומי
שינויים בשרתי 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 מספק מנגנון לפתרון התנגשויות קבוצות של משחקים שמורים בעלי מאפיינים זהים לחשבון פעיל בזמן הקריאה ומאפשרים פתרון שמתאימה למשחק שלכם.
כששירותי המשחקים של Google Play מזהים התנגשות נתונים,
השיטה SnapshotsClient.DataOrConflict.isConflict()
מחזירה את הערך true
באירוע הזה,
במחלקה SnapshotsClient.SnapshotConflict
יש שתי גרסאות של המשחק השמור:
- גרסת השרת: הגרסה העדכנית ביותר שידועה בשירותי המשחקים של Google Play והיא מדויקת למכשיר של השחקן. וגם
- גרסה מקומית: גרסה מקומית שזוהתה באחד מהמכשירים של הנגן שמכיל תוכן או מטא-נתונים בעלי מאפיינים זהים לחשבון פעיל. יכול להיות שהגרסה הזו לא זהה לגרסה שניסית לשמור.
המשחק שלך צריך להחליט איך לפתור את המחלוקת על ידי בחירה באחת מהאפשרויות שסופקו או מיזוג הנתונים של שתי הגרסאות השמורות של המשחקים.
כדי לזהות ולפתור התנגשויות בין משחקים שמורים:
- קוראים לפונקציה
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; 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 PlayGames.getSnapshotsClient(theActivity) .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)"; } } }); }
שינוי משחקים שמורים לצורך פתרון סכסוכים
אם רוצים למזג נתונים מכמה משחקים שמורים או לשנות 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
פתוח כדי שהמשחק יוכל לשנות אותו.
- אם יש התנגשות, הפונקציה