إضافة ألعاب محفوظة إلى لعبتك

يوضّح لك هذا الدليل كيفية حفظ بيانات مستوى تقدّم اللاعب في اللعبة وتحميلها باستخدام خدمة "الألعاب المحفوظة" في تطبيق C++. يمكنك استخدام هذه الخدمة لتحميل مستوى تقدّم اللاعب في اللعبة وحفظه تلقائيًا في أي وقت أثناء اللعب. يمكن أن تتيح هذه الخدمة أيضًا للاعبين تشغيل واجهة مستخدم لتعديل ملفّ حفظ لعبة حالي أو استعادته أو إنشاء ملفّ جديد.

قبل البدء

ننصحك بمراجعة مفاهيم ألعاب "حفظ التقدم في الألعاب" إذا لم يسبق لك ذلك.

قبل بدء الترميز باستخدام واجهة برمجة التطبيقات Saved Games API:

تنسيقات البيانات والتوافق مع جميع المنصات

يجب أن تكون بيانات "ألعاب Google Play المحفوظة" التي تحفظها على خوادم Google بتنسيق std::vector<uint8_t>. تتولى خدمة "الألعاب المحفوظة" ترميز بياناتك لتوافقها مع جميع المنصات. ويمكن لتطبيقات Android قراءة هذه البيانات نفسها كصفيف بايت بدون أي مشاكل في التوافق مع جميع المنصات.

تجنَّب استخدام تنسيقات خاصة بالنظام الأساسي عند اختيار تنسيق بيانات لبيانات "ألعاب محفوظة". ننصحك بشدة باستخدام تنسيق بيانات، مثل XML أو JSON، يتيح استخدام مكتبات قوية على منصات متعددة.

تفعيل خدمة "حفظ الألعاب"

قبل التمكّن من استخدام خدمة "الألعاب المحفوظة"، عليك أولاً تفعيل إمكانية الوصول إليها. لإجراء ذلك، اتصل برقم EnableSnapshots() عند إنشاء الخدمة باستخدام gpg::GameServices::Builder. سيؤدي ذلك إلى تفعيل نطاقات المصادقة الإضافية التي تتطلّبها ميزة "ألعاب محفوظة" في حدث المصادقة التالي.

عرض "حفظ التقدم في الألعاب"

يمكنك توفير خيار يمكن للاعبين تفعيله لحفظ الألعاب المحفوظة أو استعادتها في لعبتك. عندما يختار اللاعبون هذا الخيار، من المفترض أن تعرِض لعبتك شاشة تعرض خانات الحفظ الحالية، وتسمح للاعبين إما بحفظ اللعبة في إحدى هذه الخانات أو تحميلها منها، أو إنشاء لعبة محفوظة جديدة. استخدِم الخطوات التالية لإجراء ذلك:

  SnapshotManager::ShowSelectUIOperation(...)

تسمح واجهة مستخدِم اختيار "الألعاب المحفوظة" لللاعبِين بإنشاء لعبة محفوظة جديدة، وعرض تفاصيل عن الألعاب المحفوظة الحالية، وتحميل الألعاب المحفوظة السابقة.

  SnapshotManager::SnapshotSelectUIResponse response;
  if (IsSuccess(response.status)) {
  if (response.data.Valid()) {
    LogI("Description: %s", response.data.Description().c_str());
    LogI("FileName %s", response.data.FileName().c_str());
    //Opening the snapshot data
    
  } else {
    LogI("Creating new snapshot");
    
  }
} else {
  LogI("ShowSelectUIOperation returns an error %d", response.status);
}

يوضّح المثال التالي كيفية عرض واجهة المستخدم التلقائية للألعاب المحفوظة ومعالجة اختيار واجهة المستخدم الخاص باللاعب:

  service_->Snapshots().ShowSelectUIOperation(
  ALLOW_CREATE_SNAPSHOT,
  ALLOW_DELETE_SNAPSHOT,
  MAX_SNAPSHOTS,
  SNAPSHOT_UI_TITLE,
  [this](gpg::SnapshotManager::SnapshotSelectUIResponse const & response) {
  
      }

في المثال أعلاه، إذا كان ALLOW_CREATE_SNAPSHOT يساوي true وكان MAX_SNAPSHOTS أكبر من العدد الفعلي لللقطات التي أنشأها المستخدم حاليًا، يقدّم واجهة مستخدم Snapshot التلقائية للاعبين زرًا لإنشاء ملف جديد لحفظ اللعبة بدلاً من اختيار ملف حالي. (عند عرض الزر، يكون في أسفل واجهة المستخدم). عندما ينقر أحد اللاعبين على هذا الزر، يكون الردّ SnapshotSelectUIResponse صالحًا ولكن لا يحتوي على أي بيانات.

فتح الألعاب المحفوظة وقراءتها

للوصول إلى لعبة محفوظة وقراءة محتوياتها أو تعديلها، افتح أولاً عنصر SnapshotMetadata الذي يمثّل تلك اللعبة المحفوظة. بعد ذلك، استخدِم الطريقة SnapshotManager::Read*().

يوضّح المثال التالي كيفية فتح لعبة محفوظة:

  LogI("Opening file");
  service_->Snapshots()
  .Open(current_snapshot_.FileName(),
               gpg::SnapshotConflictPolicy::BASE_WINS,
        [this](gpg::SnapshotManager::OpenResponse const & response) {
           LogI("Reading file");
           gpg::SnapshotManager::ReadResponse responseRead =
           service_->Snapshots().ReadBlocking(response.data);
          
        }

رصد تعارضات البيانات وحلّها

عند فتح عنصر SnapshotMetadata، ترصد خدمة "الألعاب المحفوظة" ما إذا كانت هناك لعبة محفوظة متضاربة. قد تحدث تعارضات في البيانات عندما لا تكون ملفّات الحفظ الخاصة باللعبة على جهاز اللاعب غير متطابقة مع النسخة البعيدة التي يتم تخزينها على خوادم Google.

تُحدِّد سياسة التعارض التي تحدّدها عند فتح لعبة محفوظة للخدمة "ألعاب محفوظة" كيفية حل تعارض البيانات تلقائيًا. يمكن أن تكون السياسة إحدى السياسات التالية:

سياسة النزاعات الوصف
SnapshotConflictPolicy::MANUAL يشير إلى أنّه يجب ألا تتّخذ خدمة "ألعاب محفوظة" أي إجراء لحلّ المشكلة. وبدلاً من ذلك، ستُجري لعبتك دمجًا مخصّصًا.
SnapshotConflictPolicy::LONGEST_PLAYTIME يشير ذلك إلى أنّ خدمة "الألعاب المحفوظة" يجب أن تختار اللعبة المحفوظة التي تتضمن أكبر قيمة لوقت اللعب.
SnapshotConflictPolicy::BASE_WINS يشير ذلك إلى أنّ خدمة "الألعاب المحفوظة" يجب أن تختار ملف القاعدة للعبة المحفوظة.
SnapshotConflictPolicy::REMOTE_WINS يشير إلى أنّ خدمة "الألعاب المحفوظة" يجب أن تختار اللعبة المحفوظة عن بُعد. الإصدار البعيد هو إصدار من اللعبة المحفوظة يتم رصده على أحد أجهزة اللاعب ويحتوي على طابع زمني أحدث من الإصدار الأساسي.

إذا حدّدت سياسة تعارض غير GPGSnapshotConflictPolicyManual، ستدمج خدمة "ألعاب محفوظة" اللعبة المحفوظة وتُرجع الإصدار المعدَّل من خلال قيمة SnapshotManager::OpenResponse الناتجة. يمكن للعبة فتح اللعبة المحفوظة والكتابة فيها، ثم استدعاء SnapshotManager::Commit(...) لحفظ اللعبة المحفوظة على خوادم Google.

إجراء دمج مخصّص

إذا حدّدت SnapshotConflictPolicy::MANUAL كسياسة التعارض، يجب أن تحلّ لعبتك أي تعارض في البيانات يتم رصده قبل تنفيذ المزيد من عمليات قراءة أو كتابة في اللعبة المحفوظة.

في هذه الحالة، عند رصد تعارض في البيانات، تُرجِع الخدمة المَعلمات التالية من خلال SnapshotManager::OpenResponse:

  • conflict_id لتحديد هذا التعارض بشكل فريد (ستستخدم هذه القيمة عند إرسال الإصدار النهائي من اللعبة المحفوظة)
  • الإصدار الأساسي المتضارب من اللعبة المحفوظة
  • النسخة البعيدة المتضاربة من اللعبة المحفوظة

على لعبتك تحديد البيانات التي يجب حفظها، ثم استدعاء SnapshotManager::ResolveConflictBlocking() لحفظ/حلّ الإصدار النهائي على خوادم Google.

    //Resolve conflict
    gpg::SnapshotManager::OpenResponse resolveResponse =
        manager.ResolveConflictBlocking(openResponse.conflict_base, metadata_change,
                                  openResponse.conflict_id);

كتابة الألعاب المحفوظة

لكتابة لعبة محفوظة، افتح أولاً عنصر SnapshotMetadata الذي يمثّل هذه اللعبة المحفوظة، وحلّ أي تعارضات بيانات يتم رصدها، ثم استخدِم الأسلوب SnapshotManager::Commit() لحفظ التغييرات التي أجريتها على اللعبة المحفوظة.

يوضّح المثال التالي كيفية إجراء تغيير وحفظ ملف لعبة.

  1. أولاً، افتح اللقطة التي نريد تعديلها وتأكَّد من حلّ كل التعارضات باختيار القاعدة.

    service_->Snapshots().Open(
          file_name,
          gpg::SnapshotConflictPolicy::BASE_WINS,
          [this](gpg::SnapshotManager::OpenResponse const &response) {
            if (IsSuccess(response.status)) {
              // metadata : gpg::SnapshotMetadata
              metadata = response.data;
            } else {
              // Handle snapshot open error here
            }
          });
    
  2. بعد ذلك، أنشئ تغييرًا في لعبة محفوظة يتضمّن بيانات الصورة المستخدَمة في صورة الغلاف:

    gpg::SnapshotMetadataChange::Builder builder;
    gpg::SnapshotMetadataChange metadata_change =
        builder.SetDescription("CollectAllTheStar savedata")
                 .SetCoverImageFromPngData(pngData).Create();
    
  3. أخيرًا، احفظ التغييرات التي أجريتها على اللعبة.

    gpg::SnapshotManager::CommitResponse commitResponse =
        service_->Snapshots().CommitBlocking(metadata, metadata_change, SetupSnapshotData());
    

    تحتوي مَعلمة data على جميع بيانات حفظ الألعاب التي تخزّنها. يتضمّن التغيير أيضًا بيانات وصفية إضافية عن اللعبة المحفوظة، مثل المدّة التي تم فيها تشغيل اللعبة ووصف لها.

إذا اكتملت عملية الحفظ بنجاح، يمكن للاعبين الاطّلاع على اللعبة المحفوظة في واجهة مستخدم اختيار "الألعاب المحفوظة".