הוספת משחקים שמורים למשחק

בעקבות ההוצאה משימוש של ה-API של התחברות באמצעות חשבון Google, בשנת 2026 אנחנו מסירים את גרסה 1 של ה-SDK שהייתה זמינה למשחקים. החל מפברואר 2025, לא תהיה לך אפשרות לפרסם ב-Google Play משחקים שה-SDK בגרסה הזו שולב בהם לאחרונה. מומלץ להשתמש בגרסה 2 של ה-SDK למשחקים.
פריטים קיימים עם גרסה 1 ימשיכו לפעול בשנים הקרובות, אבל מומלץ לעבור לגרסה 2 החל מיוני 2025.
המדריך הזה מיועד לגרסה 1 של ה-SDK של Play Games Services. גרסה 2 של ה-SDK ל-C++‎ של Play Games Services עדיין לא זמינה.

במדריך הזה נסביר איך לשמור ולטעון נתוני התקדמות של שחקנים במשחק באמצעות שירות Saved Games באפליקציית C++‎. אתם יכולים להשתמש בשירות הזה כדי לטעון ולשמור באופן אוטומטי את ההתקדמות של השחקן במשחק בכל שלב במהלך המשחק. השירות הזה גם מאפשר לשחקנים להפעיל ממשק משתמש כדי לעדכן או לשחזר משחק שמור קיים, או ליצור משחק שמור חדש.

לפני שמתחילים

אם עדיין לא עשיתם זאת, ייתכן שתמצאו שזה מועיל לעיין במושגים של Saved Games.

לפני שמתחילים לכתוב קוד שמשתמשים בו בממשק ה-API של Saved Games:

פורמטים של נתונים ותאימות בפלטפורמות שונות

נתוני Saved Games שאתם שומרים בשרתים של Google צריכים להיות בפורמט std::vector<uint8_t>. שירות המשחקים השמורים דואג לקודד את הנתונים שלכם כדי שתהיה תאימות בין פלטפורמות שונות. אפליקציות ל-Android יכולות לקרוא את אותם נתונים כמערך של בייטים בלי בעיות תאימות בין פלטפורמות.

כשבוחרים פורמט נתונים לנתוני Saved Games, מומלץ להימנע משימוש בפורמטים ספציפיים לפלטפורמה. מומלץ מאוד להשתמש בפורמט נתונים כמו XML או JSON, שיש לו תמיכה חזקה בספרייה בפלטפורמות שונות.

הפעלת שירות Saved Games

כדי להשתמש בשירות 'משחקים שמורים', צריך קודם להפעיל את הגישה אליו. כדי לעשות את זה, צריך להתקשר אל EnableSnapshots() כשיוצרים את השירות באמצעות gpg::GameServices::Builder. באירוע האימות הבא, יופעלו היקפי ההרשאות הנוספים שנדרשים על ידי התכונה 'משחקים שמורים'.

הצגת משחקים שמורים

במשחק, אתם יכולים לספק לשחקנים אפשרות להפעיל שמירה או שחזור של משחקים שמורים. כששחקנים בוחרים באפשרות הזו, המשחק צריך להציג מסך עם משבצות שמירה קיימות, ולאפשר לשחקנים לשמור את המשחק באחת מהמשבצות האלה או לטעון משחק שמור מאחת מהן, או ליצור משחק שמור חדש. כדי לעשות זאת, משתמשים בשיטה הבאה:

  SnapshotManager::ShowSelectUIOperation(...)

ממשק המשתמש של בחירת Saved Games מאפשר לשחקנים ליצור Saved Game חדש, לראות פרטים על Saved Games קיימים ולטעון Saved Games קודמים.

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

בדוגמה הבאה אפשר לראות איך להציג את ממשק המשתמש של Saved Games ולטפל בבחירה של השחקן בממשק המשתמש:

  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 גדול ממספר צילומי המצב שהמשתמש יצר בפועל, ממשק המשתמש של צילום המצב שמוגדר כברירת מחדל מספק לשחקנים לחצן ליצירת שמירה חדשה של המשחק, במקום לבחור שמירה קיימת. (כשהלחצן מוצג, הוא מופיע בתחתית ממשק המשתמש). כששחקן לוחץ על הלחצן הזה, התגובה של SnapshotSelectUIResponse תקינה אבל לא מכילה נתונים.

פתיחה וקריאה של משחקים שמורים

כדי לגשת למשחק שמור ולקרוא או לשנות את התוכן שלו, קודם צריך לפתוח את אובייקט SnapshotMetadata שמייצג את המשחק השמור. לאחר מכן, מבצעים קריאה ל-method‏ 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, שירות Saved Games בודק אם קיים משחק שמור שמתנגש עם המשחק הנוכחי. יכול להיות שיהיו התנגשויות בנתונים אם המשחק השמור שמאוחסן במכשיר המקומי של השחקן לא מסונכרן עם הגרסה המרוחקת שמאוחסנת בשרתי Google.

מדיניות הטיפול בקונפליקטים שאתם מציינים כשאתם פותחים משחק שמור, מגדירה לשירות Saved Games איך לפתור קונפליקט נתונים באופן אוטומטי. המדיניות יכולה להיות אחת מהאפשרויות הבאות:

מדיניות בנושא קונפליקטים תיאור
SnapshotConflictPolicy::MANUAL מציין ששירות Saved Games לא צריך לבצע פעולת פתרון. במקום זאת, המשחק יבצע מיזוג בהתאמה אישית.
SnapshotConflictPolicy::LONGEST_PLAYTIME מציין ששירות Saved Games צריך לבחור את המשחק השמור עם ערך זמן המשחק הגדול ביותר.
SnapshotConflictPolicy::BASE_WINS מציין ששירות Saved Games צריך לבחור את המשחק השמור הבסיסי.
SnapshotConflictPolicy::REMOTE_WINS מציין ששירות המשחקים השמורים צריך לבחור את המשחק השמור מרחוק. הגרסה המרוחקת היא גרסה של המשחק השמור שמזוהה באחד מהמכשירים של השחקן, וחותמת הזמן שלה עדכנית יותר מזו של גרסת הבסיס.

אם ציינתם מדיניות אחרת לפתרון סכסוכים במקום GPGSnapshotConflictPolicyManual, שירות Saved Games ימזג את המשחק השמור ויחזיר את הגרסה המעודכנת באמצעות הערך 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());
    

    פרמטר הנתונים מכיל את כל נתוני המשחק שאתם מאחסנים. השינוי מכיל גם מטא-נתונים נוספים של המשחק השמור, כמו זמן המשחק ותיאור של המשחק השמור.

אם פעולת השמירה הושלמה בהצלחה, שחקנים יכולים לראות את המשחק השמור בממשק המשתמש של בחירת Saved Games.