การรองรับเกมที่บันทึกไว้ในเกม Android

คู่มือนี้จะแสดงวิธีใช้เกมเกมที่บันทึกไว้โดยใช้ Snapshot API จากบริการเกมของ Google Play API จะอยู่ใน com.google.android.gms.games.snapshot และแพ็กเกจ com.google.android.gms.games แพ็กเกจ

ก่อนเริ่มต้น

ซึ่งหากคุณยังไม่ได้อ่าน อาจเป็นประโยชน์ในการตรวจสอบ แนวคิดเกมที่บันทึกไว้

การรับไคลเอ็นต์สแนปชอต

ในการเริ่มใช้ Snapshot API เกมของคุณจะต้องรับ SnapshotsClient โดยการเรียกใช้ Games.getSnapshotsClient() และการส่งใน กิจกรรมและ GoogleSignInAccount สำหรับโปรแกรมเล่นปัจจุบัน ดูวิธีการ เรียกดูข้อมูลบัญชีของผู้เล่น โปรดดู ลงชื่อเข้าใช้ใน Android Games

การระบุขอบเขตไดรฟ์

Snapshot API อาศัย Google Drive API สำหรับพื้นที่เก็บข้อมูลเกมที่บันทึกไว้ ถึง เข้าถึง Drive API แอปของคุณต้องระบุ Drive.SCOPE_APPFOLDER เมื่อสร้างไคลเอ็นต์ Google Sign-In

ตัวอย่างวิธีดำเนินการใน onResume() สำหรับกิจกรรมการลงชื่อเข้าใช้

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

กำลังแสดงเกมที่บันทึกไว้

คุณสามารถผสานรวม Snapshot API ได้ทุกที่ที่เกมของคุณมอบให้ผู้เล่น ตัวเลือกในการบันทึกหรือคืนค่าความคืบหน้า เกมของคุณอาจแสดง ที่จุดบันทึก/กู้คืนที่กำหนดไว้ หรือให้ผู้เล่นบันทึกหรือคืนค่าได้ ความคืบหน้าได้ตลอดเวลา

เมื่อผู้เล่นเลือกตัวเลือกบันทึก/คืนค่าในเกมแล้ว เกมของคุณสามารถ เลือกเปิดหน้าจอที่แจ้งให้ผู้เล่นป้อนข้อมูล หรือเลือกเกมที่บันทึกไว้ที่มีอยู่แล้วเพื่อคืนค่า

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

หากผู้เล่นเลือกที่จะสร้างเกมใหม่ที่บันทึกไว้หรือโหลดเกมที่บันทึกไว้ที่มีอยู่ UI ส่งคำขอไปยังบริการเกมของ Google Play หากคำขอประสบความสำเร็จ บริการเกมของ Google Play ส่งคืนข้อมูลเพื่อสร้างหรือกู้คืนเกมที่บันทึกไว้ผ่าน onActivityResult() Callback เกมของคุณจะลบล้าง Callback นี้เพื่อตรวจสอบว่าเกิดข้อผิดพลาดระหว่างคำขอหรือไม่

ข้อมูลโค้ดต่อไปนี้แสดงตัวอย่างการใช้งาน 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. ดึงข้อมูลอินสแตนซ์ SnapshotContents ผ่าน SnapshotsClient.SnapshotConflict
  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 =
      Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(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. ดึงข้อมูลอินสแตนซ์ SnapshotContents ผ่าน SnapshotsClient.SnapshotConflict
  3. เรียกใช้ SnapshotContents.readFully() เพื่ออ่านเนื้อหาของสแนปชอต

ตัวอย่างต่อไปนี้แสดงวิธีโหลดเกมบางเกมที่บันทึกไว้

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

การจัดการความขัดแย้งของเกมที่บันทึกไว้

เมื่อใช้ Snapshot API ในเกม อาจเป็นไปได้ว่า เพื่อทำการอ่านและเขียนในเกมเดียวกันที่บันทึกไว้ ในกรณีที่ อุปกรณ์ขาดการเชื่อมต่อเครือข่ายชั่วคราวและมีการเชื่อมต่ออีกครั้งในภายหลัง ก่อให้เกิดความขัดแย้งของข้อมูลซึ่งเกมที่บันทึกไว้ซึ่งจัดเก็บไว้ในอุปกรณ์ภายในของผู้เล่น ไม่ซิงค์กับเวอร์ชันระยะไกลที่จัดเก็บไว้ในเซิร์ฟเวอร์ของ Google

Snapshot API มีกลไกการแก้ไขข้อขัดแย้งที่แสดงทั้ง ชุดเกมที่บันทึกไว้ที่ขัดแย้งกันในเวลาที่อ่าน และช่วยให้คุณแก้ไขปัญหา กลยุทธ์ที่เหมาะกับเกมของคุณ

เมื่อบริการเกมของ Google Play ตรวจพบความขัดแย้งของข้อมูล เมธอด SnapshotsClient.DataOrConflict.isConflict() จะแสดงผลค่า true ในเหตุการณ์นี้ ชั้นเรียน SnapshotsClient.SnapshotConflict มีเกมที่บันทึกไว้ 2 เวอร์ชัน ได้แก่

  • เวอร์ชันของเซิร์ฟเวอร์: เวอร์ชันล่าสุดที่บริการเกมของ Google Play ทราบว่ามีความแม่นยำ สำหรับอุปกรณ์ของโปรแกรมเล่น และ
  • เวอร์ชันในเครื่อง: เวอร์ชันที่แก้ไขแล้วซึ่งตรวจพบในอุปกรณ์ของโปรแกรมเล่นหนึ่งที่มี เนื้อหาหรือข้อมูลเมตาที่ขัดแย้งกัน ซึ่งอาจไม่ใช่เวอร์ชันที่คุณพยายามบันทึก

เกมของคุณต้องตัดสินใจว่าจะแก้ไขความขัดแย้งอย่างไรด้วยการเลือกหนึ่งใน เวอร์ชันที่มีให้ หรือรวมข้อมูลของเกมเวอร์ชันที่บันทึกไว้ 2 เวอร์ชัน

วิธีตรวจหาและแก้ไขความขัดแย้งของเกมที่บันทึกไว้

  1. โทร SnapshotsClient.open() ผลลัพธ์ของงานมีคลาส SnapshotsClient.DataOrConflict
  2. เรียกใช้เมธอด SnapshotsClient.DataOrConflict.isConflict() หากผลลัพธ์เป็น "จริง" คุณมี ความขัดแย้งที่ต้องแก้ไข
  3. โทรหา SnapshotsClient.DataOrConflict.getConflict() เพื่อเรียกข้อมูล SnaphotsClient.snapshotConflict อินสแตนซ์
  4. เรียกใช้ SnapshotsClient.SnapshotConflict.getConflictId() เพื่อเรียกข้อมูลรหัสความขัดแย้งที่ไม่ซ้ำกัน ระบุความขัดแย้งที่ตรวจพบ เกมของคุณต้องใช้ค่านี้เพื่อส่งคำขอแก้ไขข้อขัดแย้ง ในภายหลัง
  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 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");
              }
            }
          });
}

การแก้ไขเกมที่บันทึกไว้เพื่อแก้ไขความขัดแย้ง

หากต้องการรวมข้อมูลจากเกมที่บันทึกไว้หลายเกมหรือแก้ไข 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() เป็นอาร์กิวเมนต์แรก และ SnapshotMetadataChange และ SnapshotContents ออบเจ็กต์ที่คุณแก้ไขก่อนหน้านี้เป็นรายการที่ 2 และ อาร์กิวเมนต์ที่ 3 ตามลำดับ
  6. หากการเรียก SnapshotsClient.resolveConflict() สำเร็จ API จะจัดเก็บ Snapshot ไปยังเซิร์ฟเวอร์และพยายามเปิดออบเจ็กต์ Snapshot ในอุปกรณ์ของคุณเอง
    • หากมีข้อขัดแย้ง SnapshotsClient.DataOrConflict.isConflict() จะแสดงผล true ด้วยวิธีนี้ เกมของคุณควรกลับไปยังขั้นตอนที่ 2 และทำตามขั้นตอนซ้ำเพื่อแก้ไขภาพรวมจนกว่า ความขัดแย้งจะได้รับการแก้ไข
    • หากไม่มีข้อขัดแย้ง SnapshotsClient.DataOrConflict.isConflict() จะแสดงผล false และ ออบเจ็กต์ Snapshot เปิดให้เกมแก้ไขแล้ว