Android oyunları için kaydedilmiş oyunlar

Bu kılavuzda, Google Play Games Hizmetleri tarafından sağlanan anlık görüntüler API'si kullanılarak kaydedilmiş oyunların nasıl uygulanacağı gösterilmektedir. API'leri com.google.android.gms.games.snapshot ve com.google.android.gms.games paketlerinde bulabilirsiniz.

Başlamadan önce

Özellik hakkında bilgi edinmek için Kaydedilmiş Oyunlara genel bakış başlıklı makaleyi inceleyin.

Anlık görüntüler istemcisini edinme

Anlık görüntüler API'sini kullanmaya başlamak için oyununuzun önce bir SnapshotsClient nesnesi alması gerekir. Bunu, Games.getSnapshotsContents() yöntemini çağırıp etkinliği ileterek yapabilirsiniz.

Kayıtlı oyunları görüntüleme

Anlık görüntüler API'sini, oyununuzda oyunculara ilerlemelerini kaydetme veya geri yükleme seçeneği sunulan her yere entegre edebilirsiniz. Oyununuz, bu seçeneği belirlenen kaydetme veya geri yükleme noktalarında gösterebilir ya da oyuncuların ilerlemeyi istedikleri zaman kaydetmelerine veya geri yüklemelerine izin verebilir.

Oyuncular oyununuzda kaydetme veya geri yükleme seçeneğini belirlediğinde oyununuz, oyuncuları yeni bir kayıtlı oyun için bilgi girmeye ya da geri yüklemek üzere mevcut bir kayıtlı oyunu seçmeye yönlendiren bir ekran gösterebilir.

Geliştirme sürecinizi basitleştirmek için anlık görüntüler API'si, kullanıma hazır olarak kullanabileceğiniz varsayılan bir kayıtlı oyun seçimi kullanıcı arayüzü (UI) sağlar. Kayıtlı oyunlar seçim kullanıcı arayüzü, oyuncuların yeni bir kayıtlı oyun oluşturmasına, mevcut kayıtlı oyunlarla ilgili ayrıntıları görüntülemesine ve önceki kayıtlı oyunları yüklemesine olanak tanır.

Varsayılan Kaydedilmiş Oyunlar kullanıcı arayüzünü başlatmak için:

  1. Varsayılan kayıtlı oyun seçimi kullanıcı arayüzünü başlatmak için SnapshotsClient.getSelectSnapshotIntent() çağrısı yapın. Intent
  2. startActivityForResult() numaralı telefonu arayın Intent. Çağrı başarılı olursa oyun, belirtmiş olduğunuz seçeneklerle birlikte kayıtlı oyun seçimi kullanıcı arayüzünü gösterir.

Varsayılan kayıtlı oyun seçimi kullanıcı arayüzünü başlatma örneğini aşağıda görebilirsiniz:

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ü, Play Games Hizmetleri'ne bir istek gönderir. İstek başarılı olursa Play Games Hizmetleri, onActivityResult() geri çağırma işlevi aracılığıyla kayıtlı oyunu oluşturmak veya geri yüklemek için bilgileri döndürür. Oyununuz, istek sırasında hata oluşup oluşmadığını kontrol etmek için bu geri çağırmayı geçersiz kılabilir.

Aşağıdaki kod snippet'inde onActivityResult() için örnek bir uygulama gösterilmektedir:

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
      // ...
    }
  }
}

Kayıtlı oyunları yazma

İçeriği kayıtlı oyuna depolamak için:

  1. SnapshotsClient.open() kullanarak bir anlık görüntüyü eşzamansız olarak açın.

  2. SnapshotsClient.DataOrConflict.getData() işlevini çağırarak görevin sonucundan Snapshot nesnesini alın.

  3. SnapshotContents örneğini SnapshotsClient.SnapshotConflict ile alma.

  4. Oyuncunun verilerini bayt biçiminde depolamak için Call SnapshotContents.writeBytes() kullanılır.

  5. Tüm değişiklikleriniz yazıldıktan sonra SnapshotsClient.commitAndClose() işlevini çağırarak değişikliklerinizi Google'ın sunucularına gönderin. Oyununuz, yöntem çağrısında isteğe bağlı olarak Play Games Hizmetleri'ne bu kayıtlı oyunun oyunculara nasıl sunulacağını bildirmek için ek bilgiler sağlayabilir. Bu bilgiler, oyununuzun SnapshotMetadataChange.Builder kullanarak oluşturduğu bir SnapshotMetaDataChange nesnesinde gösterilir.

Aşağıdaki snippet, oyununuzun kayıtlı bir oyunda değişiklikleri nasıl işleyebileceğini gösterir:

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() işlevini çağırdığında oyuncunun cihazı bir ağa bağlı değilse Play Games Hizmetleri, kayıtlı oyun verilerini cihazda yerel olarak saklar. Cihaz yeniden bağlandığında Play Games Hizmetleri, yerel olarak önbelleğe alınan kayıtlı oyun değişikliklerini Google'ın sunucularıyla senkronize eder.

Kayıtlı oyunları yükleme

Şu anda oturum açmış oyuncu için kayıtlı oyunları almak üzere:

  1. SnapshotsClient.open() ile bir anlık görüntüyü eşzamansız olarak açın.

  2. Snapshot nesnesini, SnapshotsClient.DataOrConflict.getData() işlevini çağırarak görevin sonucundan alın. Alternatif olarak, oyununuz Kayıtlı oyunları görüntüleme bölümünde açıklandığı gibi kayıtlı oyun seçimi kullanıcı arayüzü aracılığıyla da belirli bir anlık görüntüyü alabilir.

  3. SnapshotContents örneğini SnapshotsClient.SnapshotConflict ile alın.

  4. Anlık görüntünün içeriğini okumak için SnapshotContents.readFully() işlevini çağırın.

Aşağıdaki snippet'te, belirli bir kayıtlı oyunun nasıl yüklenebileceği 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.
          // ...
        }
      });
}

Kayıtlı oyun çakışmalarını yönetme

Oyununuzda anlık görüntüler API'sini kullanırken birden fazla cihazın aynı kayıtlı oyunda okuma ve yazma işlemleri yapması mümkündür. Bir cihazın ağ bağlantısı geçici olarak kesilip daha sonra yeniden bağlanması durumunda, oyuncunun yerel cihazında depolanan kayıtlı oyunun Google'ın sunucularında depolanan uzak sürümle senkronize olmaması nedeniyle veri çakışmaları yaşanabilir.

Anlık görüntüler API'si, okuma sırasında çakışan kayıtlı oyunların her ikisini de sunan bir çakışma çözme mekanizması sağlar ve oyununuza uygun bir çözüm stratejisi uygulamanıza olanak tanır.

Play Games Hizmetleri bir veri çakışması algıladığında SnapshotsClient.DataOrConflict.isConflict() yöntemi true değerini döndürür. Bu durumda, SnapshotsClient.SnapshotConflict sınıfı, kayıtlı oyunun iki sürümünü sağlar:

  • Sunucu sürümü: Play Games Hizmetleri tarafından oyuncunun cihazı için doğru olduğu bilinen en güncel sürüm.

  • Yerel sürüm: Oynatıcının cihazlarından birinde, çakışan içerik veya meta veri içeren değiştirilmiş bir sürüm algılandı. Bu sürüm, kaydetmeye çalıştığınız sürümle aynı olmayabilir.

Oyununuz, sağlanan sürümlerden birini seçerek veya iki kayıtlı oyun sürümünün verilerini birleştirerek çakışmayı nasıl çözeceğine karar vermelidir.

Kayıtlı oyun çakışmalarını tespit etmek ve çözmek için:

  1. SnapshotsClient.open() numaralı telefonu arayın. Görev sonucu SnapshotsClient.DataOrConflict sınıfını içeriyor.

  2. SnapshotsClient.DataOrConflict.isConflict() yöntemini çağırın. Sonuç doğruysa çözmeniz gereken bir çakışma vardır.

  3. SnapshotsClient.snapshotConflict örneğini almak için SnapshotsClient.DataOrConflict.getConflict() numaralı telefonu arayın.

  4. Algılanan çakışmayı benzersiz şekilde tanımlayan çakışma kimliğini almak için Call SnapshotsClient.SnapshotConflict.getConflictId() işlemini kullanın. Oyununuzun daha sonra çakışma çözümü isteği göndermesi için bu değere ihtiyacı vardır.

  5. Yerel sürümü edinmek için SnapshotsClient.SnapshotConflict.getConflictingSnapshot() numaralı telefonu arayın.

  6. Sunucu sürümünü almak için SnapshotsClient.SnapshotConflict.getSnapshot() numaralı telefonu arayın.

  7. Kayıtlı oyun çakışmasını çözmek için sunucuya son sürüm olarak kaydetmek istediğiniz sürümü seçin ve bunu SnapshotsClient.resolveConflict() yöntemine iletin.

Aşağıdaki snippet'te, oyununuzun, kaydedilmiş oyun çakışmasını en son değiştirilen kaydedilmiş oyunu son kaydedilecek sürüm olarak seçerek nasıl ele alabileceğine dair 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");
              }
            }
          });
}

Kayıtlı oyunları değiştirme

Birden fazla kayıtlı oyundan verileri birleştirmek veya mevcut bir Snapshot dosyasını değiştirip çözümlenmiş nihai sürüm olarak sunucuya kaydetmek istiyorsanız aşağıdaki adımları uygulayın:

  1. SnapshotsClient.open() numaralı telefonu arayın.

  2. Yeni bir SnapshotContents nesnesi almak için SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent() numaralı telefonu arayın.

  3. SnapshotsClient.SnapshotConflict.getConflictingSnapshot() ve SnapshotsClient.SnapshotConflict.getSnapshot() verilerini önceki adımdaki SnapshotContents nesnesinde birleştirin.

  4. İsteğe bağlı olarak, meta veri alanlarında değişiklik varsa SnapshotMetadataChange örneği oluşturun.

  5. SnapshotsClient.resolveConflict() numaralı telefonu arayın. Yöntem çağrınızda, ilk bağımsız değişken olarak SnapshotsClient.SnapshotConflict.getConflictId(), ikinci ve üçüncü bağımsız değişkenler olarak da sırasıyla SnapshotMetadataChange ve SnapshotContents nesnelerini iletin.

  6. SnapshotsClient.resolveConflict() çağrısı başarılı olursa API, Snapshot nesnesini sunucuda depolar ve yerel cihazınızda Snapshot nesnesini açmaya çalışır.