שמירת נתונים

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

כדי להשתמש ב-Realtime Database, צריך:

  • רושמים את הפרויקט ב-Unity ומגדירים אותו לשימוש ב-Firebase.

    • אם בפרויקט ב-Unity כבר משתמשים ב-Firebase, הוא כבר רשום והוגדר ל-Firebase.

    • אם אין לכם פרויקט ב-Unity, תוכלו להוריד אפליקציה לדוגמה.

  • מוסיפים את Firebase Unity SDK (במיוחד FirebaseDatabase.unitypackage) לפרויקט ב-Unity.

חשוב לזכור שהוספת Firebase לפרויקט ב-Unity כוללת משימות גם במסוף Firebase וגם בפרויקט הפתוח ב-Unity (לדוגמה, מורידים קובצי תצורה של Firebase מהמסוף ומעבירים אותם לפרויקט ב-Unity).

שמירת נתונים

יש חמש שיטות לכתיבה של נתונים ב-Firebase Realtime Database:

שיטה שימושים נפוצים
SetValueAsync() כותבים או מחליפים נתונים בנתיב מוגדר, כמו users/<user-id>/<username>.
SetRawJsonValueAsync() כותבים או מחליפים נתונים ב-JSON גולמי, כמו users/<user-id>/<username>.
Push() הוספה לרשימת נתונים. בכל פעם שמפעילים את Push(), מערכת Firebase יוצרת מפתח ייחודי שאפשר להשתמש בו גם כמזהה ייחודי, למשל user-scores/<user-id>/<unique-score-id>.
UpdateChildrenAsync() לעדכן חלק מהמפתחות לנתיב מוגדר בלי להחליף את כל הנתונים.
RunTransaction() עדכון נתונים מורכבים שעשויים להיפגם בגלל עדכונים בו-זמניים.

אחזור של DatabaseReference

כדי לכתוב נתונים במסד הנתונים, צריך מופע של DatabaseReference:

using Firebase;
using Firebase.Database;

public class MyScript: MonoBehaviour {
  void Start() {
    // Get the root reference location of the database.
    DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference;
  }
}

כתיבת, עדכון או מחיקה של נתונים בהפניה

פעולות כתיבה בסיסיות

בשביל פעולות כתיבה בסיסיות אפשר להשתמש ב-SetValueAsync() כדי לשמור נתונים בקובץ עזר ספציפי, ולהחליף את כל הנתונים הקיימים בנתיב הזה. אפשר להשתמש בשיטה הזו כדי להעביר סוגים שתואמים לסוגי ה-JSON הזמינים, באופן הבא:

  • string
  • long
  • double
  • bool
  • Dictionary<string, Object>
  • List<Object>

אם משתמשים באובייקט מסוג C#, אפשר להשתמש ב-JsonUtility.ToJson() המובנה כדי להמיר את האובייקט ל-JSON גולמי ולקרוא ל-SetRawJsonValueAsync(). לדוגמה, נניח שיש לכם מחלקה של משתמש שנראית כך:

public class User {
    public string username;
    public string email;

    public User() {
    }

    public User(string username, string email) {
        this.username = username;
        this.email = email;
    }
}

אפשר להוסיף משתמש באמצעות SetRawJsonValueAsync() באופן הבא:

private void writeNewUser(string userId, string name, string email) {
    User user = new User(name, email);
    string json = JsonUtility.ToJson(user);

    mDatabaseRef.Child("users").Child(userId).SetRawJsonValueAsync(json);
}

שימוש ב-SetValueAsync() או ב-SetRawJsonValueAsync() באופן הזה יחליף את הנתונים במיקום שצוין, כולל כל צומתי הצאצא. עם זאת, עדיין תוכלו לעדכן צאצא בלי לכתוב מחדש את האובייקט כולו. אם רוצים לאפשר למשתמשים לעדכן את הפרופילים שלהם, אפשר לעדכן את שם המשתמש באופן הבא:

mDatabaseRef.Child("users").Child(userId).Child("username").SetValueAsync(name);

הוספה לרשימה של נתונים

אפשר להשתמש בשיטה Push() כדי לצרף נתונים לרשימה באפליקציות לריבוי משתמשים. השיטה Push() יוצרת מפתח ייחודי בכל פעם שמתווסף צאצא חדש להפניה שצוינה ב-Firebase. השימוש במפתחות האלה שנוצרים באופן אוטומטי לכל רכיב חדש ברשימה מאפשר לכמה לקוחות להוסיף צאצאים לאותו מיקום בו-זמנית, בלי התנגשויות בכתיבה. המפתח הייחודי שנוצר על ידי Push() מבוסס על חותמת זמן, כך שפריטי הרשימה ממוינים באופן אוטומטי לפי סדר כרונולוגי.

אפשר להשתמש בהפניה לנתונים החדשים שמוחזרים על ידי השיטה Push() כדי לקבל את הערך של המפתח שנוצר באופן אוטומטי של הצאצא, או כדי להגדיר נתונים עבור הצאצא. קריאה ל-Key על הפניה ל-Push() מחזירה את הערך של המפתח שנוצר באופן אוטומטי.

עדכון שדות ספציפיים

כדי לכתוב בו-זמנית לצאצאים ספציפיים של צומת בלי לשכתב צמתים צאצאים אחרים, משתמשים בשיטה UpdateChildrenAsync().

כשקוראים ל-UpdateChildrenAsync(), אפשר לעדכן ערכים של צאצאים ברמה נמוכה יותר על ידי ציון נתיב למפתח. אם הנתונים מאוחסנים במספר מיקומים כדי לשפר את יכולת ההתאמה לעומס, אפשר לעדכן את כל המופעים של הנתונים באמצעות הרחבת נתונים. לדוגמה, למשחק יכולה להיות מחלקה LeaderboardEntry כמו זו:

public class LeaderboardEntry {
    public string uid;
    public int score = 0;

    public LeaderboardEntry() {
    }

    public LeaderboardEntry(string uid, int score) {
        this.uid = uid;
        this.score = score;
    }

    public Dictionary&ltstring, Object&gt ToDictionary() {
        Dictionary&ltstring, Object&gt result = new Dictionary&ltstring, Object&gt();
        result["uid"] = uid;
        result["score"] = score;

        return result;
    }
}

כדי ליצור LeaderboardEntry ולעדכן אותו בו-זמנית בפיד של הציונים האחרונים וברשימת הציונים של המשתמש, המשחק משתמש בקוד כזה:

private void WriteNewScore(string userId, int score) {
    // Create new entry at /user-scores/$userid/$scoreid and at
    // /leaderboard/$scoreid simultaneously
    string key = mDatabase.Child("scores").Push().Key;
    LeaderBoardEntry entry = new LeaderBoardEntry(userId, score);
    Dictionary&ltstring, Object&gt entryValues = entry.ToDictionary();

    Dictionary&ltstring, Object&gt childUpdates = new Dictionary&ltstring, Object&gt();
    childUpdates["/scores/" + key] = entryValues;
    childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

    mDatabase.UpdateChildrenAsync(childUpdates);
}

בדוגמה הזו, נעשה שימוש ב-Push() כדי ליצור רשומה בצומת שמכיל את הרשומות לכל המשתמשים ב-/scores/$key, ולאחזר את המפתח בו-זמנית עם Key. לאחר מכן אפשר להשתמש במפתח כדי ליצור רשומה שנייה בציונים של המשתמש ב-/user-scores/$userid/$key.

בעזרת הנתיבים האלה אפשר לבצע עדכונים בו-זמנית במספר מיקומים בעץ ה-JSON באמצעות קריאה אחת ל-UpdateChildrenAsync(). לדוגמה, בקוד הבא נוצרת הרשומה החדשה בשני המיקומים. עדכונים בו-זמניים שמתבצעים באופן הזה הם אטומיים: כל העדכונים מתבצעים בהצלחה או שהם כולם נכשלים.

מחיקת נתונים

הדרך הפשוטה ביותר למחוק נתונים היא להפעיל את RemoveValue() על הפניה למיקום של הנתונים האלה.

אפשר גם למחוק על ידי ציון הערך null כערך של פעולת כתיבה אחרת, כמו SetValueAsync() או UpdateChildrenAsync(). אפשר להשתמש בשיטה הזו עם UpdateChildrenAsync() כדי למחוק מספר צאצאים בקריאה אחת ל-API.

לדעת מתי הנתונים שלכם מחויבים.

כדי לדעת מתי הנתונים שלכם הועברו לשרת Firebase Realtime Database, תוכלו להוסיף המשך. גם SetValueAsync() וגם UpdateChildrenAsync() מחזירים את הערך Task שמאפשר לדעת מתי הפעולה הושלמה. אם הקריאה נכשלת מסיבה כלשהי, הערך של IsFaulted ב-Tasks יהיה true והמאפיין Exception יציין את הסיבה לכישלון.

שמירת נתונים כעסקאות

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

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

private void AddScoreToLeaders(string email, 
                               long score,
                               DatabaseReference leaderBoardRef) {

    leaderBoardRef.RunTransaction(mutableData =&gt {
      List&ltobject&gt leaders = mutableData.Value as List&ltobject>

      if (leaders == null) {
        leaders = new List&ltobject&gt();
      } else if (mutableData.ChildrenCount &gt= MaxScores) {
        long minScore = long.MaxValue;
        object minVal = null;
        foreach (var child in leaders) {
          if (!(child is Dictionary&ltstring, object&gt)) continue;
          long childScore = (long)
                      ((Dictionary&ltstring, object&gt)child)["score"];
          if (childScore &lt minScore) {
            minScore = childScore;
            minVal = child;
          }
        }
        if (minScore &gt score) {
          // The new score is lower than the existing 5 scores, abort.
          return TransactionResult.Abort();
        }

        // Remove the lowest score.
        leaders.Remove(minVal);
      }

      // Add the new high score.
      Dictionary&ltstring, object&gt newScoreMap =
                       new Dictionary&ltstring, object&gt();
      newScoreMap["score"] = score;
      newScoreMap["email"] = email;
      leaders.Add(newScoreMap);
      mutableData.Value = leaders;
      return TransactionResult.Success(mutableData);
    });
}

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

כתיבת נתונים במצב אופליין

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

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

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

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

השלבים הבאים