本指南將說明如何使用
Google Play 遊戲服務的快照 API。您可以在 com.google.android.gms.games.snapshot
和 com.google.android.gms.games
套件中取得此 API。
事前準備
如果您還沒有這樣做,建議您詳閱 遊戲進度存檔概念。
- 請務必啟用遊戲進度存檔支援功能 。
- 下載並查看遊戲進度存檔程式碼範例: Android 範例頁面。
- 請熟讀 品質檢查清單。
取得快照用戶端
若要使用快照 API,遊戲必須先取得
SnapshotsClient
物件。方法是呼叫
Games.getSnapshotsClient()
方法並傳入
目前玩家的活動和 GoogleSignInAccount
。如要瞭解
擷取玩家帳戶資訊,請參閱
登入 Android 遊戲。
指定雲端硬碟範圍
快照 API 需要使用 Google Drive API 才能儲存遊戲進度存檔。目的地:
存取 Drive API,您的應用程式必須指定
Drive.SCOPE_APPFOLDER
敬上
。
以下舉例說明如何透過
onResume()
敬上
方法操作:
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 } } }); }
顯示遊戲進度存檔
您可以在遊戲的任何位置整合快照 API 儲存或還原進度的選項。您的遊戲可能會顯示這類 或讓玩家儲存或還原點數 並隨時補充進度
玩家在遊戲中選取儲存/還原選項後,遊戲就可以 可視需要顯示提示畫面,提示玩家輸入新儲存的資訊 遊戲,或是選取要還原的現有遊戲進度存檔。
為簡化開發作業,快照 API 會提供預設的遊戲進度存檔使用者選擇使用者 使用者介面 (UI)。遊戲進度存檔選擇 UI 可讓玩家 建立新的遊戲進度存檔、查看現有遊戲進度存檔,以及載入先前儲存的遊戲進度存檔。
如要啟動預設的遊戲進度存檔 UI:
- 呼叫
SnapshotsClient.getSelectSnapshotIntent()
即可Intent
用於啟動預設值 遊戲進度存檔選擇 UI。 - 呼叫
startActivityForResult()
並傳入Intent
。 如果呼叫成功,遊戲會顯示遊戲進度存檔選擇 UI 。
以下範例說明如何啟動預設的遊戲進度存檔選擇 UI:
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); } }); }
如果玩家選擇建立新的遊戲進度存檔,或載入現有的遊戲進度存檔,
UI 傳送要求至 Google Play 遊戲服務。如果要求成功
Google Play 遊戲服務會傳回資訊,以便透過以下方式建立或還原遊戲進度存檔
onActivityResult()
回呼。遊戲可能會覆寫此回呼,以確認要求期間是否發生任何錯誤。
下列程式碼片段顯示
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 // ... } } }
正在寫入遊戲進度存檔
在遊戲進度存檔中儲存內容:
- 透過
SnapshotsClient.open()
,以非同步方式開啟快照。然後,擷取Snapshot
物件 呼叫SnapshotsClient.DataOrConflict.getData()
,從工作結果的結果中呼叫。 - 透過
SnapshotsClient.SnapshotConflict
擷取SnapshotContents
執行個體。 - 呼叫
SnapshotContents.writeBytes()
,以位元組格式儲存玩家的資料。 - 寫入所有變更後,請呼叫
將變更傳送至 Google 的伺服器。
SnapshotsClient.commitAndClose()
。在方法呼叫中 您可以選擇提供額外資訊,讓 Google Play 遊戲服務 向玩家呈現這個遊戲進度存檔。這項資訊會以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 = Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(this)); // Commit the operation return snapshotsClient.commitAndClose(snapshot, metadataChange); }
應用程式呼叫時,如果玩家的裝置未連上網路
SnapshotsClient.commitAndClose()
,Google Play 遊戲服務會將遊戲進度存檔儲存在本機
裝置。一旦裝置重新連線,Google Play 遊戲服務就會同步處理本機快取的遊戲進度存檔
Google 伺服器的變化
正在載入遊戲進度存檔
擷取目前登入玩家的遊戲進度存檔:
- 透過
SnapshotsClient.open()
,以非同步方式開啟快照。然後,擷取Snapshot
物件 呼叫SnapshotsClient.DataOrConflict.getData()
,從工作結果的結果中呼叫。或者,您也可以 遊戲也能透過遊戲進度存檔選擇 UI 擷取特定快照,如 顯示遊戲進度存檔。 - 透過
SnapshotsClient.SnapshotConflict
擷取SnapshotContents
執行個體。 - 呼叫
SnapshotContents.readFully()
以讀取快照的內容。
下列程式碼片段說明如何載入特定的遊戲進度存檔:
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. // ... } }); }
處理遊戲進度存檔衝突
在遊戲中使用快照 API 時, 讓裝置以同一遊戲進度存檔執行讀取和寫入作業。如果 裝置的網路連線暫時中斷,稍後又重新連線,這可能 將遊戲進度存檔儲存在玩家的本機裝置上,會造成資料衝突 與 Google 伺服器中儲存的遠端版本不同步。
快照 API 提供衝突解決機制,可顯示 且可在閱讀時提供衝突的遊戲進度存檔 定義遊戲所需的策略
當 Google Play 遊戲服務偵測到資料衝突時,
SnapshotsClient.DataOrConflict.isConflict()
方法會傳回 true
的值。在此事件中,
SnapshotsClient.SnapshotConflict
類別提供兩種遊戲進度存檔:
- 伺服器版本:Google Play 遊戲服務已知的最新版本,以提供準確資訊 玩家的裝置;和
- 本機版本:在其中一部玩家裝置中偵測到修改過的版本, 相衝突的內容或中繼資料這可能與您嘗試儲存的版本不同。
您的遊戲必須決定如何解決衝突,選擇以下其中一種方法: 或合併兩個遊戲進度存檔版本的資料。
偵測並解決遊戲進度存檔衝突問題:
- 呼叫
SnapshotsClient.open()
。工作結果包含SnapshotsClient.DataOrConflict
類別。 - 呼叫
SnapshotsClient.DataOrConflict.isConflict()
方法。如果結果為 true, 問題解決。 - 呼叫
SnapshotsClient.DataOrConflict.getConflict()
以擷取SnaphotsClient.snapshotConflict
執行個體。 - 呼叫
SnapshotsClient.SnapshotConflict.getConflictId()
擷取不重複的衝突 ID 就會識別偵測到的衝突您的遊戲需要使用這個值傳送衝突解決要求 - 呼叫
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 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)"; } } }); }
修改遊戲進度存檔以解決衝突
如何合併多個遊戲進度存檔的資料或修改現有的Snapshot
如要在伺服器中儲存為已解決的最終版本,請按照下列步驟操作:
- 呼叫
SnapshotsClient.open()
。 - 呼叫
SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent()
以取得新的SnapshotContents
物件。 - 合併
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
與 從SnapshotsClient.SnapshotConflict.getSnapshot()
傳入SnapshotContents
物件 上一個步驟 - 視需要建立
SnapshotMetadataChange
執行個體 (如果中繼資料有任何變更) 只要使用來自這些領域的 小型資料集訓練即可 - 呼叫
SnapshotsClient.resolveConflict()
。在方法呼叫中,傳入SnapshotsClient.SnapshotConflict.getConflictId()
做為第一個引數,而 您之前修改的SnapshotMetadataChange
和SnapshotContents
物件,變成第二項並 。 - 如果
SnapshotsClient.resolveConflict()
呼叫成功,API 會儲存Snapshot
物件,並嘗試在本機裝置中開啟快照物件。- 如果有衝突,
SnapshotsClient.DataOrConflict.isConflict()
會傳回true
。在本 遊戲就會回到步驟 2,然後重複上述步驟來修改快照,直到 就會解決衝突 - 如果沒有衝突,
SnapshotsClient.DataOrConflict.isConflict()
會傳回false
並Snapshot
物件可供遊戲修改。
- 如果有衝突,