本指南將說明如何使用
Google Play 遊戲服務的快照 API。如需 API,請前往
com.google.android.gms.games.snapshot
敬上
和
com.google.android.gms.games
。
套件
事前準備
如需關於功能的詳細資訊,請參閱 遊戲進度存檔總覽。
- 啟用遊戲進度存檔支援功能
- 下載並查看遊戲進度存檔程式碼範例: Android 範例頁面。
- 請熟讀 品質檢查清單。
取得快照用戶端
若要使用快照 API,遊戲必須先取得
SnapshotsClient
物件。要取得此物件,您可以呼叫 Games.getSnapshotsClient()
方法,然後在活動內傳遞。
指定雲端硬碟範圍
快照 API 需要使用 Google Drive API
遊戲進度存檔如要存取 Drive API,應用程式必須指定
Drive.SCOPE_APPFOLDER
敬上
。
以下舉例說明如何透過
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 儲存或還原進度的選項。您的遊戲可能會顯示這類 或讓玩家儲存或還原點數 並隨時補充進度
玩家在遊戲中選取儲存/還原選項後,遊戲就可以 可視需要顯示提示畫面,提示玩家輸入新儲存的資訊 遊戲,或是選取要還原的現有遊戲進度存檔。
為簡化開發作業,快照 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 = 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); } }); }
如果玩家選擇建立新的遊戲進度存檔,或載入現有的遊戲進度存檔,
UI 傳送要求給 Play 遊戲服務。如果要求成功
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()
。致電
SnapshotContents.writeBytes()
敬上 以位元組格式儲存玩家的資料。寫入所有變更後,請呼叫
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 = PlayGames.getSnapshotsClient(this); // Commit the operation return snapshotsClient.commitAndClose(snapshot, metadataChange); }
應用程式呼叫時,如果玩家的裝置未連上網路
SnapshotsClient.commitAndClose()
、
Play 遊戲服務會將遊戲進度存檔資料儲存在本機裝置。在裝置上
Play 遊戲服務會將本機快取的遊戲進度存檔同步變更為重新連線
Google 伺服器。
載入遊戲進度存檔
擷取目前登入玩家的遊戲進度存檔:
以非同步方式開啟快照
SnapshotsClient.open()
。開張
Snapshot
敬上 將物件從工作結果的SnapshotsClient.DataOrConflict.getData()
。此外,遊戲也可以擷取 透過遊戲進度存檔選擇 UI 建立快照, 顯示遊戲進度存檔。致電
SnapshotContents.readFully()
敬上 讀取快照內容
下列程式碼片段說明如何載入特定的遊戲進度存檔:
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. // ... } }); }
處理遊戲進度存檔衝突
在遊戲中使用快照 API 時, 讓裝置以同一遊戲進度存檔執行讀取和寫入作業。如果 裝置的網路連線暫時中斷,稍後又重新連線,這可能 將遊戲進度存檔儲存在玩家的本機裝置上,會造成資料衝突 與 Google 伺服器中儲存的遠端版本不同步。
快照 API 提供衝突解決機制,可顯示 且可在閱讀時提供衝突的遊戲進度存檔 定義遊戲所需的策略
當 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; 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.SnapshotConflict.getResolutionSnapshotsContent()
即可取得新的抵免額SnapshotContents
物件。合併下列來源的資料
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
敬上 和SnapshotsClient.SnapshotConflict.getSnapshot()
放入SnapshotContents
物件(選用)
SnapshotMetadataChange
敬上 例如中繼資料欄位是否有任何變更致電
SnapshotsClient.resolveConflict()
。 在方法呼叫中,將SnapshotsClient.SnapshotConflict.getConflictId()
做為第一個引數,而SnapshotMetadataChange
。 和 您稍早修改的SnapshotContents
物件,並將 。如果
SnapshotsClient.resolveConflict()
敬上 呼叫成功,API 會將Snapshot
物件儲存至伺服器, 會嘗試開啟本機裝置上的 Snapshot 物件。- 如果有衝突,
SnapshotsClient.DataOrConflict.isConflict()
會傳回true
。在本 遊戲就會回到步驟 2,然後重複上述步驟來修改快照,直到 就會解決衝突 - 如果沒有衝突
SnapshotsClient.DataOrConflict.isConflict()
會傳回false
,且Snapshot
物件也會開放,可供遊戲修改。
- 如果有衝突,