Android 게임의 저장된 게임 지원

이 가이드에서는 Snapshots API를 추가합니다. API는 com.google.android.gms.games.snapshotcom.google.android.gms.games 패키지에서 찾을 수 있습니다.

시작하기 전에

아직 검토하지 않은 경우 저장된 게임 게임 개념

스냅샷 클라이언트 가져오기

Snapshots API를 사용하려면 먼저 게임에서 SnapshotsClient 객체를 가져와야 합니다. 이렇게 하려면 Games.getSnapshotsClient() 메서드를 호출하고 활동을 전달하면 됩니다.

Drive 범위 지정

Snapshots API는 저장된 게임 저장용량에 Google Drive API를 사용합니다. 받는사람 Drive API에 액세스하려면 앱에서 Drive.SCOPE_APPFOLDER 범위를 지정할 수 있습니다

다음은 로그인 활동의 onResume() 메서드에서 이를 실행하는 방법을 보여주는 예입니다.


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

저장된 게임 표시

게임에서 플레이어에게 진행 상황을 저장하거나 복원하는 옵션을 제공할 때마다 Snapshots API를 통합할 수 있습니다. 게임은 지정된 저장/복원 지점에 이러한 옵션을 표시하거나 플레이어가 언제든지 진행 상황을 저장하거나 복원하도록 허용할 수 있습니다.

플레이어가 게임에서 저장/복원 옵션을 선택하면 게임에서는 선택적으로 새로 저장된 게임의 정보를 입력하거나 복원할 기존 저장된 게임을 선택하라는 메시지가 담긴 화면을 표시할 수 있습니다.

개발을 간소화하기 위해 Snapshots API는 즉시 사용할 수 있는 기본 저장된 게임 선택 사용자 인터페이스(UI)를 제공합니다. 저장된 게임 선택 UI를 사용하면 플레이어가 새 저장된 게임을 만들고 기존 저장된 게임에 관한 세부정보를 확인하고 이전에 저장된 게임을 로드할 수 있습니다.

기본 저장된 게임 UI를 실행하려면 다음 단계를 따르세요.

  1. SnapshotsClient.getSelectSnapshotIntent()를 호출하여 Intent: 기본값을 실행합니다. 저장된 게임 선택 UI
  2. startActivityForResult()를 호출합니다. 해당 Intent를 전달합니다. 호출이 성공하면 저장된 게임 선택 UI가 지정된 옵션과 함께 게임에 표시됩니다.

다음은 기본 저장된 게임 선택 UI를 실행하는 방법을 보여주는 예입니다.

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

플레이어가 저장된 게임을 새로 만들거나 기존에 저장된 게임을 로드하도록 선택하면 UI가 Google Play 게임즈 서비스에 요청을 보냅니다. 요청이 성공하면 Google Play 게임즈 서비스는 onActivityResult() 있습니다. 게임에서는 이 콜백을 재정의하여 요청 중에 오류가 발생했는지 확인할 수 있습니다.

다음 코드 스니펫은 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
      // ...
    }
  }
}

저장된 게임 작성하기

저장된 게임에 콘텐츠를 저장하려면 다음 단계를 따르세요.

  1. SnapshotsClient.open()를 통해 스냅샷을 비동기적으로 엽니다. 그런 다음 Snapshot 객체를 가져옵니다. SnapshotsClient.DataOrConflict.getData()를 호출하여 작업 결과에서 삭제할 수 있습니다.
  2. SnapshotsClient.SnapshotConflict를 통해 SnapshotContents 인스턴스를 검색합니다.
  3. SnapshotContents.writeBytes()를 호출하여 플레이어의 데이터를 바이트 형식으로 저장합니다.
  4. 모든 변경사항이 작성되면 SnapshotsClient.commitAndClose(): 변경사항을 Google 서버로 전송합니다. 메서드 호출에서 필요에 따라 Google Play 게임즈 서비스에 표시할 수 있습니다. 이 정보는 SnapshotMetaDataChange 객체가 됩니다. 이 객체는 게임에서 SnapshotMetadataChange.Builder를 사용하여 만듭니다.

다음 스니펫은 게임에서 변경사항을 저장된 게임에 커밋할 수 있는 방법을 보여줍니다.

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

앱에서 호출할 때 플레이어의 기기가 네트워크에 연결되어 있지 않은 경우 SnapshotsClient.commitAndClose()님, Google Play 게임즈 서비스는 저장된 게임 데이터를 로컬 컴퓨터에 저장합니다. 있습니다. 기기가 다시 연결되면 Google Play 게임즈 서비스가 로컬에 캐시된 저장된 게임을 동기화합니다. Google 서버 변경사항의 영향을 받습니다

저장된 게임 로드 중

현재 로그인된 플레이어의 저장된 게임을 가져오려면 다음 단계를 따르세요.

  1. SnapshotsClient.open()를 통해 스냅샷을 비동기적으로 엽니다. 그런 다음 Snapshot 객체를 가져옵니다. SnapshotsClient.DataOrConflict.getData()를 호출하여 작업 결과에서 삭제할 수 있습니다. 또는 또한 저장된 게임 선택 UI를 통해 특정 스냅샷을 가져올 수도 있습니다. 저장된 게임 표시.
  2. SnapshotsClient.SnapshotConflict를 통해 SnapshotContents 인스턴스를 가져옵니다.
  3. SnapshotContents.readFully()를 호출하여 스냅샷의 콘텐츠를 읽습니다.

다음 스니펫은 저장된 특정 게임을 로드할 수 있는 방법을 보여줍니다.

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

저장된 게임 충돌 처리

게임에서 Snapshots API를 사용하면 여러 기기가 하나의 저장된 게임에서 읽기 및 쓰기를 실행할 수 있습니다. 기기의 네트워크 연결이 일시적으로 끊어졌다가 나중에 다시 연결되면 플레이어의 로컬 기기에 저장된 게임이 Google 서버에 저장된 원격 버전과 동기화되지 않는 데이터 충돌이 발생할 수 있습니다.

Snapshots API는 읽기 시간에 두 세트의 충돌하는 저장된 게임을 모두 표시하고 게임에 적합한 해결 전략을 구현할 수 있도록 하는 충돌 해결 메커니즘을 제공합니다.

Google Play 게임즈 서비스에서 데이터 충돌을 감지하면 SnapshotsClient.DataOrConflict.isConflict() 메서드는 true 값을 반환합니다. 이 경우 SnapshotsClient.SnapshotConflict 클래스는 저장된 게임의 두 가지 버전을 제공합니다.

  • 서버 버전: Google Play 게임즈 서비스에서 정확한 것으로 알려진 최신 버전 플레이어 기기의 경우 및
  • 로컬 버전: 플레이어의 기기 중 하나에서 감지된 수정된 버전은 충돌할 수 있는 콘텐츠 또는 메타데이터가 있습니다. 저장하려는 버전과 동일하지 않을 수 있습니다.

게임에서는 제공된 버전 중 하나를 선택하거나 두 가지 저장된 게임 버전의 데이터를 병합하여 충돌을 해결하는 방법을 결정해야 합니다.

저장된 게임 충돌을 감지하고 해결하려면 다음 단계를 따르세요.

  1. SnapshotsClient.open()을 호출합니다. 작업 결과에는 SnapshotsClient.DataOrConflict 클래스가 포함되어 있습니다.
  2. SnapshotsClient.DataOrConflict.isConflict() 메서드를 호출합니다. 결과가 참이면 있습니다.
  3. SnapshotsClient.DataOrConflict.getConflict()를 호출하여 SnaphotsClient.snapshotConflict 인스턴스
  4. SnapshotsClient.SnapshotConflict.getConflictId()를 호출하여 고유하게 사용하는 충돌 ID를 가져옵니다. 감지된 충돌을 식별합니다. 게임에서 충돌 해결 요청을 보내려면 이 값이 필요합니다. 확인할 수 있습니다
  5. SnapshotsClient.SnapshotConflict.getConflictingSnapshot()를 호출하여 로컬 버전을 가져옵니다.
  6. SnapshotsClient.SnapshotConflict.getSnapshot()를 호출하여 서버 버전을 가져옵니다.
  7. 저장된 게임 충돌을 해결하려면 SnapshotsClient.resolveConflict() 메서드에 전달합니다.

다음 스니펫은 저장할 최종 버전으로 가장 최근에 수정된 저장된 게임을 선택하여 게임이 저장된 게임 충돌을 처리할 수 있는 방법의 예를 보여줍니다.


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

충돌 해결을 위해 저장된 게임 수정

저장된 여러 게임에서 데이터를 병합하거나 기존 Snapshot을 수정하려는 경우 결정된 최종 버전으로 서버에 저장하려면 다음 단계를 따르세요.

  1. SnapshotsClient.open()를 호출합니다 .
  2. SnapshotsClient.SnapshotConflict.getResolutionSnapshotsContent()를 호출하여 SnapshotContents 객체.
  3. SnapshotsClient.SnapshotConflict.getConflictingSnapshot()의 데이터를 병합하고 SnapshotsClient.SnapshotConflict.getSnapshot()SnapshotContents 이전 단계로 넘어갑니다.
  4. 필요한 경우 메타데이터가 변경된 경우 SnapshotMetadataChange 인스턴스를 만듭니다. 있습니다.
  5. SnapshotsClient.resolveConflict()을 호출합니다. 메서드 호출에서 첫 번째 인수로 SnapshotsClient.SnapshotConflict.getConflictId() 및 앞서 수정한 SnapshotMetadataChangeSnapshotContents 객체를 두 번째 및 세 번째 인수를 사용합니다.
  6. SnapshotsClient.resolveConflict() 호출이 성공하면 API에서 Snapshot를 저장합니다. 객체를 서버에 보내고 로컬 기기에서 스냅샷 객체를 열려고 시도합니다.