Hướng dẫn này cho bạn biết cách triển khai trò chơi đã lưu bằng cách sử dụng
API ảnh chụp nhanh do Dịch vụ trò chơi của Google Play cung cấp. Bạn có thể tìm thấy API trong các gói
com.google.android.gms.games.snapshot
và
com.google.android.gms.games
.
Trước khi bắt đầu
Nếu chưa từng làm như vậy, bạn có thể xem lại Khái niệm trò chơi trong Trò chơi đã lưu.
- Đảm bảo bật tính năng hỗ trợ trò chơi đã lưu cho trò chơi của bạn trong Google Play Console.
- Tải xuống và xem lại mã mẫu trò chơi đã lưu trên Trang mẫu Android.
- Làm quen với những đề xuất được mô tả trong Danh mục kiểm tra chất lượng.
Tải ứng dụng tổng quan nhanh
Để có thể sử dụng API ảnh chụp nhanh, trước tiên, trò chơi của bạn phải có được một đối tượng
SnapshotsClient
. Bạn có thể thực hiện việc này bằng cách gọi hàm
Games.getSnapshotsClient()
và truyền vào giá trị
hoạt động và GoogleSignInAccount
cho trình phát hiện tại. Để tìm hiểu cách
truy xuất thông tin tài khoản người chơi, xem
Đăng nhập vào trò chơi trên Android.
Chỉ định phạm vi Drive
API ảnh chụp nhanh dựa vào API Google Drive để lưu trữ trò chơi đã lưu. Người nhận
truy cập API Drive, ứng dụng của bạn phải chỉ định
Drive.SCOPE_APPFOLDER
khi xây dựng ứng dụng đăng nhập bằng Google.
Dưới đây là ví dụ về cách thực hiện việc này trong phương thức
onResume()
cho hoạt động đăng nhập của bạn:
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 } } }); }
Đang hiện các trò chơi đã lưu
Bạn có thể tích hợp API ảnh chụp nhanh bất cứ khi nào trò chơi của bạn cung cấp cho người chơi tùy chọn lưu hoặc khôi phục tiến trình của họ. Trò chơi của bạn có thể hiển thị tùy chọn đó tại các điểm lưu/khôi phục được chỉ định, hoặc cho phép người chơi lưu hoặc khôi phục tiến trình bất cứ lúc nào.
Sau khi người chơi chọn tùy chọn lưu/khôi phục cho trò chơi của mình, trò chơi sẽ hiển thị một màn hình nhắc người chơi nhập thông tin cho trò chơi đã lưu mới (nếu muốn), hoặc chọn một trò chơi đã lưu hiện có để khôi phục.
Để đơn giản hóa quá trình phát triển, API ảnh chụp nhanh cung cấp giao diện người dùng (UI) lựa chọn trò chơi đã lưu mặc định mà bạn có thể sử dụng ngay. Giao diện người dùng chọn trò chơi đã lưu cho phép người chơi tạo trò chơi đã lưu mới, xem chi tiết về trò chơi đã lưu hiện có và tải những trò chơi đã lưu trước đó.
Cách mở giao diện người dùng mặc định cho Trò chơi đã lưu:
- Gọi
SnapshotsClient.getSelectSnapshotIntent()
để nhậnIntent
để chạy chế độ cài đặt mặc định giao diện người dùng chọn trò chơi đã lưu. - Gọi
startActivityForResult()
và truyền vàoIntent
đó. Nếu cuộc gọi thành công, trò chơi sẽ hiển thị giao diện người dùng cho trò chơi đã lưu, cùng với các tùy chọn mà bạn chỉ định.
Dưới đây là một ví dụ về cách khởi chạy giao diện người dùng lựa chọn trò chơi đã lưu mặc định:
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); } }); }
Nếu người chơi chọn tạo trò chơi đã lưu mới hoặc tải trò chơi đã lưu hiện có,
giao diện người dùng gửi một yêu cầu đến dịch vụ trò chơi của Google Play. Nếu yêu cầu thành công,
Dịch vụ trò chơi của Google Play trả về thông tin để tạo hoặc khôi phục trò chơi đã lưu thông qua
onActivityResult()
. Trò chơi của bạn có thể ghi đè lệnh gọi lại này để kiểm tra xem liệu có lỗi nào xảy ra trong yêu cầu hay không.
Đoạn mã sau đây cho thấy quy trình triển khai mẫu của
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 // ... } } }
Đang ghi trò chơi đã lưu
Cách lưu trữ nội dung vào trò chơi đã lưu:
- Mở ảnh chụp nhanh không đồng bộ qua
SnapshotsClient.open()
. Sau đó, hãy truy xuất đối tượngSnapshot
trong kết quả của tác vụ bằng cách gọiSnapshotsClient.DataOrConflict.getData()
. - Truy xuất một thực thể
SnapshotContents
quaSnapshotsClient.SnapshotConflict
. - Gọi
SnapshotContents.writeBytes()
để lưu trữ dữ liệu của trình phát ở định dạng byte. - Sau khi viết tất cả các thay đổi, hãy gọi
SnapshotsClient.commitAndClose()
để gửi nội dung thay đổi đến máy chủ của Google. Trong lệnh gọi phương thức, trò chơi của bạn có thể cung cấp thêm thông tin (không bắt buộc) để cho dịch vụ trò chơi của Google Play biết cách hiển thị trò chơi đã lưu này cho người chơi. Thông tin này được thể hiện trongSnapshotMetaDataChange
mà trò chơi của bạn tạo bằngSnapshotMetadataChange.Builder
.
Đoạn mã sau đây cho biết cách trò chơi của bạn có thể áp dụng thay đổi cho trò chơi đã lưu:
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); }
Nếu thiết bị của người chơi không được kết nối mạng khi ứng dụng của bạn gọi
SnapshotsClient.commitAndClose()
, Dịch vụ trò chơi của Google Play lưu trữ cục bộ dữ liệu trò chơi đã lưu trên
thiết bị. Sau khi kết nối lại thiết bị, các dịch vụ trò chơi của Google Play sẽ đồng bộ hoá trò chơi đã lưu vào bộ nhớ đệm trên máy
những thay đổi này đối với máy chủ của Google.
Đang tải trò chơi đã lưu
Cách truy xuất trò chơi đã lưu cho người chơi hiện đang đăng nhập:
- Mở ảnh chụp nhanh không đồng bộ qua
SnapshotsClient.open()
. Sau đó, hãy truy xuất đối tượngSnapshot
trong kết quả của tác vụ bằng cách gọiSnapshotsClient.DataOrConflict.getData()
. Ngoài ra, cũng có thể truy xuất thông tin tổng quan nhanh cụ thể thông qua giao diện người dùng chọn trò chơi đã lưu, như mô tả trong Hiển thị Trò chơi đã lưu. - Truy xuất thực thể
SnapshotContents
thông quaSnapshotsClient.SnapshotConflict
. - Gọi hàm
SnapshotContents.readFully()
để đọc nội dung của bản tổng quan nhanh.
Đoạn mã sau đây minh họa cách tải trò chơi đã lưu cụ thể:
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. // ... } }); }
Xử lý xung đột với trò chơi đã lưu
Khi sử dụng API ảnh chụp nhanh trong ứng dụng trò chơi, nhiều thiết bị có thể đọc và ghi trên cùng một trò chơi đã lưu. Trong trường hợp thiết bị tạm thời mất kết nối mạng và sau đó kết nối lại, việc này có thể gây ra xung đột dữ liệu khi trò chơi đã lưu trên thiết bị cục bộ của người chơi không đồng bộ với phiên bản lưu trữ từ xa trong máy chủ của Google.
API ảnh chụp nhanh cung cấp cơ chế giải quyết xung đột đại diện cho cả hai nhóm trò chơi đã lưu xung đột nhau tại thời điểm đọc, đồng thời cho phép bạn triển khai chiến lược xử lý phù hợp với trò chơi của mình.
Khi dịch vụ trò chơi của Google Play phát hiện xung đột dữ liệu,
Phương thức SnapshotsClient.DataOrConflict.isConflict()
trả về giá trị true
Trong sự kiện này, phương thức
Lớp SnapshotsClient.SnapshotConflict
cung cấp 2 phiên bản trò chơi đã lưu:
- Phiên bản máy chủ: Phiên bản mới nhất mà Dịch vụ trò chơi của Google Play xác định là chính xác cho thiết bị của người chơi; và
- Phiên bản cục bộ: Một phiên bản sửa đổi được phát hiện trên một trong các thiết bị của người chơi có chứa nội dung hoặc siêu dữ liệu xung đột. Phiên bản này có thể không giống với phiên bản mà bạn muốn lưu.
Để giải quyết xung đột, trò chơi của bạn phải quyết định chọn một trong những phiên bản được cung cấp hoặc hợp nhất dữ liệu của hai phiên bản trò chơi đã lưu.
Cách phát hiện và giải quyết các xung đột trò chơi đã lưu:
- Gọi
SnapshotsClient.open()
. Kết quả tác vụ phải chứa một lớpSnapshotsClient.DataOrConflict
. - Gọi phương thức
SnapshotsClient.DataOrConflict.isConflict()
. Nếu kết quả là đúng, bạn có xung đột để giải quyết. - Gọi
SnapshotsClient.DataOrConflict.getConflict()
để truy xuất một Thực thểSnaphotsClient.snapshotConflict
. - Gọi
SnapshotsClient.SnapshotConflict.getConflictId()
để truy xuất mã xung đột duy nhất xác định xung đột đã phát hiện. Trò chơi của bạn cần có giá trị này để gửi yêu cầu giải quyết xung đột sau. - Gọi
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
để nhận phiên bản cục bộ. - Hãy gọi lệnh
SnapshotsClient.SnapshotConflict.getSnapshot()
để lấy phiên bản máy chủ. - Để giải quyết xung đột trò chơi đã lưu, hãy chọn phiên bản bạn muốn lưu vào máy chủ làm
phiên bản cuối cùng rồi truyền vào phương thức
SnapshotsClient.resolveConflict()
.
Đoạn mã sau đây giải thích và đưa ra ví dụ về cách trò chơi của bạn xử lý các xung đột qua việc chọn trò chơi đã lưu được sửa đổi gần đây nhất làm phiên bản cuối cùng để lưu:
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)"; } } }); }
Sửa đổi trò chơi đã lưu để giải quyết xung đột
Nếu bạn muốn hợp nhất dữ liệu từ nhiều trò chơi đã lưu hoặc sửa đổi một Snapshot
hiện có
để lưu vào máy chủ dưới dạng phiên bản cuối cùng đã được giải quyết, hãy làm theo các bước sau:
- Gọi
SnapshotsClient.open()
. - Gọi
SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent()
để nhận mã mớiSnapshotContents
. - Hợp nhất dữ liệu từ
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
vàSnapshotsClient.SnapshotConflict.getSnapshot()
vào đối tượngSnapshotContents
từ bước trước đó. - Nếu muốn, hãy tạo một phiên bản
SnapshotMetadataChange
nếu có bất kỳ thay đổi nào đối với siêu dữ liệu mới. - Gọi
SnapshotsClient.resolveConflict()
. Trong lệnh gọi phương thức, hãy chuyển vàoSnapshotsClient.SnapshotConflict.getConflictId()
làm đối số đầu tiên và Các đối tượngSnapshotMetadataChange
vàSnapshotContents
mà bạn đã sửa đổi trước đó vào lần thứ hai và đối số thứ ba tương ứng. - Nếu lệnh gọi
SnapshotsClient.resolveConflict()
thành công, API sẽ lưu trữSnapshot
đến máy chủ và cố gắng mở đối tượng Tổng quan nhanh trên thiết bị cục bộ của bạn.- Nếu có xung đột,
SnapshotsClient.DataOrConflict.isConflict()
sẽ trả vềtrue
. Trong trường hợp này, trò chơi của bạn sẽ quay lại bước 2 và lặp lại các bước để sửa đổi ảnh chụp nhanh cho đến khi các xung đột được giải quyết. - Nếu không có xung đột,
SnapshotsClient.DataOrConflict.isConflict()
sẽ trả vềfalse
và đối tượngSnapshot
đang mở để trò chơi của bạn sửa đổi.
- Nếu có xung đột,