שמירת נתונים

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

לפני שמשתמשים Realtime Database צריך:

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

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

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

  • מוסיפים את ה-SDK Firebase Unity (באופן ספציפי, 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;
    }
}

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

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<string, Object> entryValues = entry.ToDictionary();

    Dictionary<string, Object> childUpdates = new Dictionary<string, Object>();
    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 ייערך ב- מאפיין 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 מסנכרן את הנתונים האלה עם שרתי מסדי הנתונים המרוחקים ועם לקוחות אחרים על בסיס 'לפי יכולת'.

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

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

השלבים הבאים