เกมที่บันทึกไว้สำหรับเกม Android

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

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

ดูข้อมูลเกี่ยวกับฟีเจอร์นี้ได้ที่ภาพรวม ของเกมที่บันทึกไว้

รับไคลเอ็นต์ Snapshots

หากต้องการเริ่มใช้ Snapshots API เกมของคุณต้องได้รับออบเจ็กต์ SnapshotsClient ก่อน คุณทำได้โดยการเรียกใช้ Games.getSnapshotsContents() เมธอดและส่งกิจกรรม

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

คุณสามารถผสานรวม 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 จะส่งคำขอไปยังบริการเกมของ Play หากคำขอสำเร็จ บริการเกมของ 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()

  2. ดึงข้อมูล Snapshot ออบเจ็กต์จากผลลัพธ์ของงานโดยเรียกใช้ SnapshotsClient.DataOrConflict.getData()

  3. ดึงข้อมูลอินสแตนซ์ SnapshotContents ด้วย SnapshotsClient.SnapshotConflict

  4. เรียกใช้ SnapshotContents.writeBytes() เพื่อจัดเก็บข้อมูลของผู้เล่นในรูปแบบไบต์

  5. เมื่อเขียนการเปลี่ยนแปลงทั้งหมดแล้ว ให้เรียกใช้ SnapshotsClient.commitAndClose() เพื่อส่ง การเปลี่ยนแปลงไปยังเซิร์ฟเวอร์ของ 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(), บริการเกมของ Play จะจัดเก็บข้อมูลเกมที่บันทึกไว้ในอุปกรณ์ เมื่ออุปกรณ์เชื่อมต่ออีกครั้ง บริการเกมของ Play จะซิงค์การเปลี่ยนแปลงเกมที่บันทึกไว้ซึ่งแคชไว้ในเครื่องกับเซิร์ฟเวอร์ของ Google

โหลดเกมที่บันทึกไว้

วิธีดึงข้อมูลเกมที่บันทึกไว้สำหรับผู้เล่นที่ผ่านการตรวจสอบสิทธิ์แล้ว

  1. เปิดสแนปชอตแบบอะซิงโครนัสด้วย SnapshotsClient.open()

  2. ดึงข้อมูล Snapshot ออบเจ็กต์จากผลลัพธ์ของงานโดยเรียกใช้ SnapshotsClient.DataOrConflict.getData() หรือเกมของคุณยังสามารถดึงข้อมูลสแนปชอตที่เฉพาะเจาะจงผ่าน UI การเลือกเกมที่บันทึกไว้ตามที่อธิบายไว้ใน แสดงเกมที่บันทึก ไว้

  3. ดึงข้อมูล SnapshotContents อินสแตนซ์ด้วย SnapshotsClient.SnapshotConflict

  4. เรียกใช้ 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 มีกลไกการแก้ปัญหาความขัดแย้งที่แสดงเกมที่บันทึกไว้ทั้ง 2 ชุดที่ขัดแย้งกันในเวลาอ่าน และช่วยให้คุณใช้กลยุทธ์การแก้ปัญหาที่เหมาะสมกับเกมของคุณได้

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

  • เวอร์ชันเซิร์ฟเวอร์: เวอร์ชันล่าสุดที่บริการเกมของ Play ทราบ ว่าถูกต้องสำหรับอุปกรณ์ของผู้เล่น

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

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

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

  1. เรียกใช้ SnapshotsClient.open() ผลลัพธ์ของงานจะมีคลาส SnapshotsClient.DataOrConflict

  2. เรียกใช้เมธอด SnapshotsClient.DataOrConflict.isConflict() หากผลลัพธ์เป็น "จริง" แสดงว่าคุณต้องแก้ปัญหาความขัดแย้ง

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

  6. หากการ SnapshotsClient.resolveConflict() เรียกใช้สำเร็จ API จะจัดเก็บออบเจ็กต์ Snapshot ลงในเซิร์ฟเวอร์และ พยายามเปิดออบเจ็กต์ Snapshot ในอุปกรณ์ในเครื่อง

    • หากมีความขัดแย้ง SnapshotsClient.DataOrConflict.isConflict() จะแสดงผล true ในกรณีนี้ เกมของคุณควรกลับไปที่ขั้นตอนที่ 2 และทำตามขั้นตอนซ้ำเพื่อแก้ไขสแนปชอตจนกว่าจะแก้ปัญหาความขัดแย้งได้
    • หากไม่มีความขัดแย้ง SnapshotsClient.DataOrConflict.isConflict() แสดงผล false และออบเจ็กต์ Snapshot จะเปิดขึ้นเพื่อให้เกมของคุณ แก้ไขได้