Bu kılavuzda, kayıtlı oyun oyununun Google Play oyun hizmetleri tarafından sağlanan anlık görüntü API'sını kullanarak nasıl uygulanacağı gösterilmektedir. API'ler com.google.android.gms.games.snapshot
ve com.google.android.gms.games
paketlerinde bulunabilir.
Başlamadan önce
Henüz yapmadıysanız Kayıtlı Oyunlar oyun kavramlarını incelemeniz faydalı olabilir.
- Google Play Console'da oyununuz için kayıtlı oyunlar desteğini etkinleştirdiğinizden emin olun.
- Kaydedilmiş oyunlar kodu örneğini Android örnekleri sayfasından indirip inceleyin.
- Kalite Kontrol Listesi'nde açıklanan öneriler hakkında bilgi edinin.
Anlık görüntü istemcisi alınıyor
Anlık görüntüler API'sini kullanmaya başlamak için oyununuzun öncelikle bir SnapshotsClient
nesnesi edinmesi gerekir. Bunu yapmak için Games.getSnapshotsClient()
yöntemini çağırıp etkinliği iletebilirsiniz.
Drive kapsamını belirtme
Snapshot API'si, kayıtlı oyun depolama alanı için Google Drive API'yi kullanır. Drive API'ye erişmek için uygulamanızda, Google ile oturum açma istemcisi oluşturulurken Drive.SCOPE_APPFOLDER
kapsamı belirtilmelidir.
Aşağıda, oturum açma etkinliğiniz için onResume()
yönteminde bunu nasıl yapacağınıza dair bir örnek verilmiştir:
@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 } } }); }
Kayıtlı oyunlar gösteriliyor
Oyununuzun ilerlemesini kaydetme veya geri yükleme seçeneği sunan oyunlar API'si, anlık görüntü API'siyle entegre edilebilir. Oyununuzda, belirtilen kaydetme/geri yükleme noktalarında böyle bir seçenek gösterilebilir veya oyuncuların istedikleri zaman ilerlemelerini kaydetmelerine veya geri yüklemelerine izin verilebilir.
Oyuncular oyununuzda kaydet/geri yükle seçeneğini belirledikten sonra, oyununuz, isteğe bağlı olarak oyunculardan yeni bir kaydedilmiş oyunla ilgili bilgileri girmelerini veya geri yüklenecek mevcut bir kayıtlı oyunları seçmelerini isteyen bir ekran açabilir.
Geliştirmenizi kolaylaştırmak için anlık görüntüler API'si, varsayılan olarak kullanabileceğiniz kayıtlı bir oyun seçimi kullanıcı arayüzü sağlar. Kayıtlı oyunlar seçimi kullanıcı arayüzü, yeni bir kayıtlı oyun oluşturma, mevcut kayıtlı oyunlarla ilgili ayrıntıları görüntüleme ve önceki kayıtlı oyunları yükleme.
Varsayılan Kaydedilmiş Oyunlar kullanıcı arayüzünü başlatmak için:
- Varsayılan kaydedilmiş oyun seçimi kullanıcı arayüzünü başlatmak üzere
Intent
almak içinSnapshotsClient.getSelectSnapshotIntent()
numaralı telefonu arayın. startActivityForResult()
numaralı telefonu arayın ve söz konusuIntent
numarasını iletin. Görüşme başarılı olursa oyun, belirttiğiniz seçeneklerle birlikte kayıtlı oyun seçimi kullanıcı arayüzünü gösterir.
Varsayılan kaydedilmiş oyun seçimi kullanıcı arayüzünün nasıl başlatılacağına dair bir örneği aşağıda bulabilirsiniz:
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); } }); }
Oyuncu yeni bir kayıtlı oyun oluşturmayı veya mevcut bir kayıtlı oyunu yüklemeyi seçerse kullanıcı arayüzü, Google Play oyun hizmetlerine bir istek gönderir. İstek başarılı olursa Google Play oyun hizmetleri, kayıtlı oyunu oluşturmak veya geri yüklemek için onActivityResult()
geri çağırması yoluyla bilgi döndürür. Oyununuz, bu geri çağırmayı geçersiz kılabilir ve istek sırasında hata olup olmadığını kontrol edebilir.
Aşağıdaki kod snippet'i, onActivityResult()
özelliğinin örnek uygulamasını göstermektedir:
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 // ... } } }
Kaydedilmiş oyunlar yazma
Kaydedilmiş bir oyuna içerik depolamak için:
SnapshotsClient.open()
aracılığıyla eşzamansız olarak bir anlık görüntü açın. Ardından,SnapshotsClient.DataOrConflict.getData()
yöntemini çağırarak görevin sonucundanSnapshot
nesnesini alın.SnapshotsClient.SnapshotConflict
aracılığıylaSnapshotContents
örneği alın.- Oynatıcının verilerini bayt biçiminde depolamak için
SnapshotContents.writeBytes()
yöntemini çağırın. - Tüm değişikliklerinizi yazdıktan sonra değişikliklerinizi
Google sunucularına göndermek için
SnapshotsClient.commitAndClose()
numaralı telefonu arayın. Yöntem çağrısında oyununuz, isteğe bağlı olarak Google Play oyun hizmetlerine oyunculara bu kayıtlı oyunu nasıl sunacağını bildirmek için ek bilgiler sağlayabilir. Bu bilgiler, oyununuzunSnapshotMetadataChange.Builder
kullanarak oluşturduğu birSnapshotMetaDataChange
nesnesinde sunulur.
Aşağıdaki snippet, oyununuzun kayıtlı bir oyunda nasıl değişiklik yapabileceğini göstermektedir:
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); }
Uygulamanız SnapshotsClient.commitAndClose()
çağırırken oyuncunun cihazı bir ağa bağlı değilse Google Play oyun hizmetleri, kayıtlı oyun verilerini cihazda yerel olarak depolar. Google Play oyun hizmetleri, cihaz yeniden bağlandıktan sonra yerel olarak önbelleğe alınan kaydedilmiş oyun değişikliklerini Google sunucularıyla senkronize eder.
Kaydedilmiş oyunlar yükleniyor
Şu anda oturum açmış olan oyuncunun kaydedilmiş oyunlarını almak için:
SnapshotsClient.open()
aracılığıyla eşzamansız olarak bir anlık görüntü açın. Ardından,SnapshotsClient.DataOrConflict.getData()
yöntemini çağırarak görevin sonucundanSnapshot
nesnesini alın. Alternatif olarak oyununuz, kayıtlı oyunlar seçim kullanıcı arayüzü aracılığıyla Kaydedilen Oyunları Görüntüleme bölümünde açıklandığı gibi belirli bir anlık görüntüyü de alabilir.SnapshotsClient.SnapshotConflict
aracılığıylaSnapshotContents
örneğini alın.- Anlık görüntünün içeriğini okumak için
SnapshotContents.readFully()
numaralı telefonu arayın.
Aşağıdaki snippet'te, belirli bir kayıtlı oyunu nasıl yükleyebileceğiniz gösterilmektedir:
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. // ... } }); }
Kaydedilen oyun çakışmalarını işleme
Oyununuzda anlık görüntüler API'sini kullanırken birden fazla cihaz aynı kayıtlı oyunda okuma ve yazma işlemi gerçekleştirebilir. Bir cihazın ağ bağlantısını geçici olarak kaybetmesi ve daha sonra yeniden bağlanması, oyuncunun yerel cihazında depolanan kayıtlı oyunun Google sunucularında depolanan uzak sürümle senkronize olmamasına neden olabilecek veri çakışmalarına neden olabilir.
Snapshot API, her iki okuma sırasında hem çakışan kayıtlı oyunları sunan hem de oyununuz için uygun olan bir çözüm stratejisi uygulamanıza olanak tanıyan bir anlaşmazlık çözümü mekanizması sunar.
Google Play oyun hizmetleri bir veri çakışması algıladığında SnapshotsClient.DataOrConflict.isConflict()
yöntemi true
değerini döndürür. Bu etkinlikte SnapshotsClient.SnapshotConflict
sınıfı kayıtlı oyunun iki sürümünü sunar:
- Sunucu sürümü: Google Play oyun hizmetleri tarafından oyuncunun cihazı için doğru olduğunun bilindiği en güncel sürüm
- Yerel sürüm: Oyuncunun cihazlarından birinde çakışan içerik veya meta veri içeren değiştirilmiş bir sürüm. Bu sürüm, kaydetmeye çalıştığınız sürümle aynı olmayabilir.
Oyununuz, sağlanan sürümlerden birini seçerek veya kayıtlı iki oyun sürümünün verilerini birleştirerek çakışmanın nasıl çözüleceğine karar vermelidir.
Kaydedilmiş oyun çakışmalarını tespit etmek ve çözmek için:
SnapshotsClient.open()
numaralı telefonu arayın. Görev sonucu birSnapshotsClient.DataOrConflict
sınıfı içeriyor.SnapshotsClient.DataOrConflict.isConflict()
yöntemini çağırın. Sonuç doğruysa çözeceğiniz bir çakışma vardır.- Bir
SnaphotsClient.snapshotConflict
örneği almak içinSnapshotsClient.DataOrConflict.getConflict()
yöntemini çağırın. - Algılanan çatışmayı benzersiz şekilde tanımlayan çakışma kimliğini almak için
SnapshotsClient.SnapshotConflict.getConflictId()
numaralı telefonu arayın. Daha sonra anlaşmazlık çözümü isteği göndermek için oyununuzun bu değere ihtiyacı vardır. - Yerel sürümü almak için
SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
numaralı telefonu arayın. - Sunucu sürümünü almak için
SnapshotsClient.SnapshotConflict.getSnapshot()
numaralı telefonu arayın. - Kaydedilen oyun çakışmasını çözmek için nihai sürüm olarak sunucuya kaydetmek istediğiniz bir sürümü seçin ve bunu
SnapshotsClient.resolveConflict()
yöntemine iletin.
Aşağıdaki snippet'te, kaydedilen son oyunu kaydetmek için son oyunun seçili oyununı seçerek oyununuzun kaydedilmiş bir oyun çakışmasını nasıl yönetebileceğiyle ilgili bir örnek gösterilmektedir:
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"); } } }); }
Kaydedilen oyunların anlaşmazlık çözümü için değiştirilmesi
Birden fazla kayıtlı oyundan alınan verileri birleştirmek veya çözümlenmiş nihai sürüm olarak sunucuya kaydetmek için mevcut bir Snapshot
üzerinde değişiklik yapmak istiyorsanız aşağıdaki adımları uygulayın:
SnapshotsClient.open()
numaralı telefonu arayın.- Yeni bir
SnapshotContents
nesnesi almak içinSnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent()
yöntemini çağırın. SnapshotsClient.SnapshotConflict.getConflictingSnapshot()
veSnapshotsClient.SnapshotConflict.getSnapshot()
kaynaklı verileri, önceki adımda elde edilenSnapshotContents
nesnesinde birleştirin.- İsteğe bağlı olarak, meta veri alanlarında herhangi bir değişiklik olması halinde bir
SnapshotMetadataChange
örneği oluşturun. SnapshotsClient.resolveConflict()
numaralı telefonu arayın. Yöntem çağrınızda ilk bağımsız değişken olarakSnapshotsClient.SnapshotConflict.getConflictId()
, ikinci ve üçüncü bağımsız değişkenler olarak daha önce değiştirdiğinizSnapshotMetadataChange
veSnapshotContents
nesneleri iletin.SnapshotsClient.resolveConflict()
çağrısı başarılı olursa API,Snapshot
nesnesini sunucuya depolar ve anlık görüntüyü nesnesini yerel cihazınızda açmayı dener.- Çakışma varsa
SnapshotsClient.DataOrConflict.isConflict()
true
değerini döndürür. Bu durumda oyununuz, 2. adıma dönmeli ve çakışmalar çözülene kadar anlık görüntüyü değiştirme adımlarını tekrarlamalıdır. - Çakışma yoksa
SnapshotsClient.DataOrConflict.isConflict()
false
özelliğini döndürür veSnapshot
nesnesinin oyununuzda değişmesi gerekir.
- Çakışma varsa