Android oyunları için kaydedilmiş oyunlar

Bu kılavuzda, Google Play Games Hizmetleri tarafından sağlanan anlık görüntü API'sini kullanarak kayıtlı oyunların nasıl uygulanacağı gösterilmektedir. API'ler com.google.android.gms.games.snapshot ve com.google.android.gms.games paketlerinde bulunabilir.

Başlamadan önce

Bu özellik hakkında bilgi edinmek için Kaydedilmiş Oyunlara genel bakış sayfasını inceleyin.

Anlık görüntü istemcisini alma

Snapshot API'yi kullanmaya başlamak için oyununuzun önce bir SnapshotsClient nesnesi edinmesi gerekir. Bunu, Games.getSnapshotsClient() yöntemini çağırarak ve etkinliği ileterek yapabilirsiniz.

Drive kapsamını belirtme

Snapshot API, kaydedilen oyunların depolanması için Google Drive API'yi kullanır. Drive API'ye erişmek için uygulamanızın Google ile Oturum Açma istemcisini oluştururken Drive.SCOPE_APPFOLDER kapsamını belirtmesi gerekir.

Oturum açma etkinliğiniz için onResume() yönteminde bunu nasıl yapacağınıza dair bir örneği aşağıda bulabilirsiniz:


@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
          }
        }
      });
}

Kaydedilmiş oyunları gösterme

Oyununuzun oyunculara ilerlemelerini kaydetme veya geri yükleme seçeneği sunduğu her yerde Snapshot API'sini entegre edebilirsiniz. Oyununuz belirlenen kaydetme/geri yükleme noktalarında böyle bir seçenek gösterebilir veya oyuncuların istedikleri zaman ilerleme durumunu kaydetmelerine veya geri yüklemelerine izin verebilir.

Oyuncular oyununuzda kaydet/geri yükleme seçeneğini belirlediğinde, oyununuz isteğe bağlı olarak oyuncuların yeni kayıtlı oyun için bilgi girmelerini veya geri yüklemek üzere mevcut bir oyunu seçmelerini isteyen bir ekran açabilir.

Snaps API, geliştirmenizi basitleştirmek için kullanıma hazır bir şekilde kullanabileceğiniz varsayılan bir kayıtlı oyun seçimi kullanıcı arayüzü (UI) sağlar. Kaydedilen oyunlar seçimi 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 önceden kaydedilmiş 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 Intent numaralı telefonu almak üzere SnapshotsClient.getSelectSnapshotIntent() numaralı telefonu arayın.
  2. startActivityForResult() numarasını arayın ve Intent kartınızı geçirin. Çağrı başarılı olursa oyun, belirttiğiniz seçeneklerle birlikte kaydedilmiş oyun seçimi kullanıcı arayüzünü görüntüler.

Kaydedilmiş oyun seçimi için varsayılan 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ü, Play Games Hizmetleri'ne bir istek gönderir. İstek başarılı olursa Play Games Hizmetleri, onActivityResult() geri çağırması aracılığıyla kayıtlı oyunu oluşturmak veya geri yüklemek için gereken 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
      // ...
    }
  }
}

Kaydedilmiş oyunları yaz

Kaydedilmiş bir oyunda içerik depolamak için:

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

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

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

  4. Oynatıcının verilerini bayt biçiminde depolamak için SnapshotContents.writeBytes() çağrısı yapın.

  5. Tüm değişiklikleriniz yazıldıktan sonra değişikliklerinizi Google'ın sunucularına göndermek için SnapshotsClient.commitAndClose() çağrısı yapın. Yöntem çağrısında, oyununuz isteğe bağlı olarak Play Games Hizmetleri'ne kaydedilen bu oyunu oyunculara nasıl sunacağını bildirmek için ek bilgiler sağlayabilir. Bu bilgiler, oyununuzun SnapshotMetadataChange.Builder kullanarak oluşturduğu bir SnapshotMetaDataChange nesnesinde temsil edilir.

Aşağıdaki snippet'te, oyununuzun kayıtlı bir oyunda nasıl değişiklik yapabileceği gösterilmektedir:

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ısı yaptığında oyuncunun cihazı bir ağa bağlı değilse Play Games Hizmetleri, kaydedilen oyun verilerini cihazda yerel olarak depolar. Cihaz yeniden bağlandıktan sonra Play Games Hizmetleri, yerel olarak önbelleğe alınmış kayıtlı oyun değişikliklerini Google'ın sunucularıyla senkronize eder.

Kaydedilmiş oyunları yükle

Şu anda oturum açmış olan oyuncunun kayıtlı oyunlarını almak için:

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

  2. SnapshotsClient.DataOrConflict.getData() yöntemini çağırarak görevin sonucundan Snapshot nesnesini alın. Alternatif olarak, oyununuz Kaydedilmiş Oyunları Görüntüleme bölümünde açıklandığı gibi, kayıtlı oyunlar seçim kullanıcı arayüzü aracılığıyla belirli bir anlık görüntü 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() çağrısı yapı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.
          // ...
        }
      });
}

Kaydedilmiş oyun çakışmalarını ele alma

Oyununuzda Snapshot API'sini kullanırken birden fazla cihaz, kaydedilen aynı oyun üzerinde okuma ve yazma işlemleri gerçekleştirebilir. Bir cihazın ağ bağlantısını geçici olarak kaybedip daha sonra yeniden bağlanması halinde bu durum, oyuncunun yerel cihazında depolanan kayıtlı oyunun, Google'ın sunucularında depolanan uzak sürümle senkronize edilmemesine ve dolayısıyla veri çakışmalarına neden olabilir.

Snapshot API'si, çakışan kaydedilmiş oyun gruplarının her ikisini de okuma sırasında sunan ve oyununuz için uygun bir çözüm stratejisi uygulayabilmenizi sağlayan bir çakışma çözüm mekanizması sağlar.

Play Games Hizmetleri bir veri çakışması tespit ettiğinde SnapshotsClient.DataOrConflict.isConflict() yöntemi true değerini döndürür. Bu durumda SnapshotsClient.SnapshotConflict sınıfı, kaydedilen oyunun iki sürümünü sunar:

  • Sunucu sürümü: Play Oyun 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 barındıran değiştirilmiş bir sürüm. Bu, kaydetmeye çalıştığınız sürümle aynı olmayabilir.

Oyununuz, sağlanan sürümlerden birini seçerek veya kaydedilen 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 edip çözmek için:

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

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

  3. SnaphotsClient.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 SnapshotsClient.SnapshotConflict.getConflictId() çağrısı yapın. Oyununuz daha sonra çakışma çözümü isteği göndermek için bu değere ihtiyaç duyar.

  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. Kaydedilmiş oyun çakışmasını çözmek için sunucuya son sürüm olarak kaydetmek istediğiniz sürümü seçip SnapshotsClient.resolveConflict() yöntemine iletin.

Aşağıdaki snippet'te, en son değiştirilen oyunun kaydedileceği son sürüm olarak seçilerek kaydedilen bir oyun çakışmasını nasıl ele alabileceği 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");
              }
            }
          });
}

Kaydedilmiş oyunları değiştirme

Kaydedilmiş birden fazla oyundaki verileri birleştirmek veya mevcut bir Snapshot dosyasını çözülmüş son sürüm olarak sunucuya kaydetmek için değiştirmek isterseniz 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() çağrısı yapın.

  3. SnapshotsClient.SnapshotConflict.getConflictingSnapshot() ve SnapshotsClient.SnapshotConflict.getSnapshot() verilerini önceki adımda bulunan SnapshotContents nesnesiyle birleştirin.

  4. İsteğe bağlı olarak, meta veri alanlarında herhangi bir değişiklik varsa bir 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() ve daha önce değiştirdiğiniz SnapshotMetadataChange ve SnapshotContents nesnelerini sırasıyla ikinci ve üçüncü bağımsız değişken olarak iletin.

  6. SnapshotsClient.resolveConflict() çağrısı başarılı olursa API, Snapshot nesnesini sunucuya depolar ve yerel cihazınızda Anlık Görüntü nesnesini açmayı dener.