Android oyunlarında kaydedilmiş oyunlar desteği

Google ile Giriş API'sinin desteğinin sonlandırılmasının ardından, 2026'da Games v1 SDK'sını kaldırıyoruz. Şubat 2025'ten sonra games v1 SDK'sıyla yeni entegre edilen oyunları Google Play'de yayınlayamayacaksınız. Bunun yerine games v2 SDK'sını kullanmanızı öneririz.
Önceki Games v1 entegrasyonlarına sahip mevcut uygulamalar birkaç yıl daha çalışmaya devam edecek olsa da Haziran 2025'ten itibaren v2'ye geçiş yapmanız önerilir.
Bu kılavuz, Play Games Hizmetleri v1 SDK'sının kullanımıyla ilgilidir. En son SDK sürümü hakkında bilgi için v2 belgelerine bakın.

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

Başlamadan önce

Henüz yapmadıysanız Kayıtlı Oyunlar oyun kavramlarını incelemeniz faydalı olabilir.

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.getSnapshotsClient() yöntemini çağırıp etkinliği ve mevcut oyuncu için GoogleSignInAccount değerini ileterek yapabilirsiniz. Oyuncu hesabı bilgilerini nasıl alacağınızı öğrenmek için Android oyunlarında oturum açma başlıklı makaleyi inceleyin.

Drive kapsamını belirtme

Anlık görüntüler API'si, kaydedilmiş oyunların depolanması için Google Drive API'yi kullanır. Drive API'ye erişmek için uygulamanız, Google ile oturum açma istemcisini oluştururken Drive.SCOPE_APPFOLDER kapsamını belirtmelidir.

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

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

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, belirlenen kaydetme/geri yükleme noktalarında böyle bir seçenek gösterebilir veya oyuncuların ilerlemeyi istedikleri zaman kaydetmelerine ya da geri yüklemelerine izin verebilir.

Oyuncular oyununuzda kaydetme/geri yükleme seçeneğini belirlediğinde oyununuz, oyuncuları yeni bir kayıtlı oyun için bilgi girmeye veya 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, varsayılan bir kayıtlı oyun seçimi kullanıcı arayüzü (UI) sağlar. Bu arayüzü hemen kullanabilirsiniz. Kayıtlı oyun 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 ö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() numaralı telefonu arayarak Intent isteyin.
  2. startActivityForResult() numaralı telefonu arayın ve Intent kodunu iletin. Arama başarılı olursa oyunda, belirttiğiniz seçeneklerle birlikte kayıtlı oyun seçimi kullanıcı arayüzü gösterilir.

Varsayılan kayıtlı 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 =
      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);
    }
  });
}

Oyuncu yeni bir kayıtlı oyun oluşturmayı veya mevcut bir kayıtlı oyunu yüklemeyi seçerse kullanıcı arayüzü, Google Play Games Hizmetleri'ne istek gönderir. İstek başarılı olursa Google Play Games Hizmetleri, onActivityResult() geri çağırma işlevi aracılığıyla kayıtlı oyunu oluşturmak veya geri yüklemek için bilgi 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() ile eşzamansız olarak anlık görüntü açın. Ardından, SnapshotsClient.DataOrConflict.getData() işlevini çağırarak görev sonucundan Snapshot nesnesini alın.
  2. SnapshotsClient.SnapshotConflict üzerinden SnapshotContents örneğini alma.
  3. Oyuncunun verilerini bayt biçiminde depolamak için SnapshotContents.writeBytes() işlevini çağırın.
  4. Tüm değişiklikleriniz yazıldıktan sonra SnapshotsClient.commitAndClose() işlevini çağırarak değişikliklerinizi Google'ın sunucularına gönderin. Yöntem çağrısında oyununuz, Google Play Games Hizmetleri'ne bu kayıtlı oyunu oyunculara nasıl sunacağını bildirmek için isteğe bağlı olarak 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 =
      Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(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 Google Play Games Hizmetleri, kayıtlı oyun verilerini cihazda yerel olarak saklar. Cihaz yeniden bağlandığında Google 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 eşzamansız olarak anlık görüntü açın. Ardından, SnapshotsClient.DataOrConflict.getData() işlevini çağırarak görev sonucundan Snapshot nesnesini alın. Alternatif olarak, oyununuz Kayıtlı 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üyü de alabilir.
  2. SnapshotContents örneğini SnapshotsClient.SnapshotConflict üzerinden alın.
  3. Anlık görüntünün içeriğini okumak için SnapshotContents.readFully() numarasını arayı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 =
      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.
          // ...
        }
      });
}

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ğlandığında, oyuncunun yerel cihazında depolanan kayıtlı oyunun Google'ın sunucularında depolanan uzak sürümüyle 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.

Google 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ü: Google Play Games Hizmetleri'nin, oyuncunun cihazı için doğru olduğunu bildiği en güncel sürüm ve
  • Yerel sürüm: Oyuncunun 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.DataOrConflict.getConflict() numaralı telefonu arayarak SnapshotsClient.snapshotConflict örneğini alın.
  4. Algılanan çakışmayı benzersiz şekilde tanımlayan çakışma kimliğini almak için SnapshotsClient.SnapshotConflict.getConflictId() işlevini çağırı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 SnapshotsClient.resolveConflict() yöntemine iletin.

Aşağıdaki snippet'te, en son değiştirilen kayıtlı oyunu kaydedilecek son sürüm olarak seçerek oyununuzun kayıtlı oyun çakışmasını 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 Games.getSnapshotsClient(theActivity, GoogleSignIn.getLastSignedInAccount(this))
      .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");
              }
            }
          });
}

Çakışma çözümü için kayıtlı oyunları değiştirme

Birden fazla kayıtlı oyundan gelen verileri birleştirmek veya mevcut bir Snapshot dosyasını değiştirerek çö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 SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent() nesnesi almak için SnapshotContents numaralı telefonu arayın.
  3. SnapshotsClient.SnapshotConflict.getConflictingSnapshot() ve SnapshotsClient.SnapshotConflict.getSnapshot() kaynaklarındaki verileri ö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 daha önce değiştirdiğiniz 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.