يوضّح لك هذا الدليل كيفية حفظ بيانات مستوى تقدّم اللاعب في اللعبة وتحميلها باستخدام خدمة "الألعاب المحفوظة" في تطبيق C++. يمكنك استخدام هذه الخدمة لتحميل مستوى تقدّم اللاعب في اللعبة وحفظه تلقائيًا في أي وقت أثناء اللعب. يمكن أن تتيح هذه الخدمة أيضًا للاعبين تشغيل واجهة مستخدم لتعديل ملفّ حفظ لعبة حالي أو استعادته أو إنشاء ملفّ جديد.
قبل البدء
ننصحك بمراجعة مفاهيم ألعاب "حفظ التقدم في الألعاب" إذا لم يسبق لك ذلك.
قبل بدء الترميز باستخدام واجهة برمجة التطبيقات Saved Games API:
- ثبِّت حزمة تطوير البرامج (SDK) لألعاب Play التي تستخدم لغة C++.
- إعداد بيئة تطوير C++
- نزِّل نموذج رمز C++ وراجِعه.
- فعِّل خدمة "الألعاب المحفوظة" في Google Play Console.
تنسيقات البيانات والتوافق مع جميع المنصات
يجب أن تكون بيانات "ألعاب 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()
لحفظ التغييرات التي أجريتها على
اللعبة المحفوظة.
يوضّح المثال التالي كيفية إجراء تغيير وحفظ ملف لعبة.
أولاً، افتح اللقطة التي نريد تعديلها وتأكَّد من حلّ كل التعارضات باختيار القاعدة.
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 } });
بعد ذلك، أنشئ تغييرًا في لعبة محفوظة يتضمّن بيانات الصورة المستخدَمة في صورة الغلاف:
gpg::SnapshotMetadataChange::Builder builder; gpg::SnapshotMetadataChange metadata_change = builder.SetDescription("CollectAllTheStar savedata") .SetCoverImageFromPngData(pngData).Create();
أخيرًا، احفظ التغييرات التي أجريتها على اللعبة.
gpg::SnapshotManager::CommitResponse commitResponse = service_->Snapshots().CommitBlocking(metadata, metadata_change, SetupSnapshotData());
تحتوي مَعلمة data على جميع بيانات حفظ الألعاب التي تخزّنها. يتضمّن التغيير أيضًا بيانات وصفية إضافية عن اللعبة المحفوظة، مثل المدّة التي تم فيها تشغيل اللعبة ووصف لها.
إذا اكتملت عملية الحفظ بنجاح، يمكن للاعبين الاطّلاع على اللعبة المحفوظة في واجهة مستخدم اختيار "الألعاب المحفوظة".