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

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

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

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

ดาวน์โหลดไคลเอ็นต์สแนปชอต

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

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

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

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

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

เมื่อบริการเกม 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 ให้เกมของคุณ แก้ไขได้