บันทึกข้อมูล

ก่อนเริ่มต้น

คุณต้องทำสิ่งต่อไปนี้ก่อนจึงจะใช้ Realtime Database ได้

  • ลงทะเบียนโปรเจ็กต์ Unity และกำหนดค่าให้ใช้ Firebase

    • หากโปรเจ็กต์ Unity ใช้ Firebase อยู่แล้ว แสดงว่าโปรเจ็กต์ดังกล่าวลงทะเบียนและกำหนดค่าสำหรับ Firebase แล้ว

    • หากไม่มีโปรเจ็กต์ Unity คุณสามารถดาวน์โหลดแอปตัวอย่างได้

  • เพิ่ม Firebase Unity SDK (โดยเฉพาะ FirebaseDatabase.unitypackage) ลงในโปรเจ็กต์ Unity

โปรดทราบว่าการเพิ่ม Firebase ลงในโปรเจ็กต์ Unity เกี่ยวข้องกับงานทั้งในคอนโซล Firebase และในโปรเจ็กต์ Unity ที่เปิดอยู่ (เช่น คุณดาวน์โหลดไฟล์การกำหนดค่า Firebase จากคอนโซล แล้วย้ายไฟล์เหล่านั้นไปยังโปรเจ็กต์ Unity)

กำลังบันทึกข้อมูล

การเขียนข้อมูลลงใน Firebase Realtime Database มี 5 วิธีดังนี้

วิธีการ การใช้งานทั่วไป
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() ได้ เช่น คุณอาจมีคลาส User ที่มีลักษณะดังนี้

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<string, Object> ToDictionary() {
        Dictionary<string, Object> result = new Dictionary<string, Object>();
        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<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 จากนั้นจะใช้คีย์เพื่อสร้างรายการที่ 2 ในคะแนนของผู้ใช้ที่ /user-scores/$userid/$key ได้

เมื่อใช้เส้นทางเหล่านี้ คุณจะอัปเดตหลายตำแหน่งในโครงสร้าง JSON พร้อมกันได้ด้วยการเรียกใช้ UpdateChildrenAsync() เพียงครั้งเดียว เช่น วิธีที่ตัวอย่างนี้สร้างรายการใหม่ในทั้ง 2 ตำแหน่ง การอัปเดตพร้อมกันที่ทำด้วยวิธีนี้จะเป็นแบบอะตอม กล่าวคือ การอัปเดตทั้งหมดจะสำเร็จหรือล้มเหลวทั้งหมด

ลบข้อมูล

วิธีที่ง่ายที่สุดในการลบข้อมูลคือการเรียกใช้ RemoveValue() ในการอ้างอิงไปยังตำแหน่งของข้อมูลนั้น

นอกจากนี้ คุณยังลบได้โดยระบุ null เป็นค่าสำหรับการดำเนินการเขียนอื่นๆ เช่น SetValueAsync() หรือ UpdateChildrenAsync() คุณสามารถใช้เทคนิคนี้กับ UpdateChildrenAsync() เพื่อลบรายการย่อยหลายรายการในการเรียก API ครั้งเดียว

รู้ว่าข้อมูลของคุณได้รับการคอมมิตเมื่อใด

หากต้องการทราบว่าระบบส่งข้อมูลไปยังเซิร์ฟเวอร์ Firebase Realtime Database เมื่อใด คุณสามารถเพิ่มการดำเนินการต่อได้ ทั้ง SetValueAsync() และ UpdateChildrenAsync() จะแสดงผล Task ที่ช่วยให้คุณทราบเมื่อการดำเนินการเสร็จสมบูรณ์ หากการเรียกใช้ไม่สำเร็จไม่ว่าด้วยเหตุผลใดก็ตาม Tasks IsFaulted จะเป็นจริงพร้อมกับพร็อพเพอร์ตี้ Exception ที่ระบุสาเหตุของความล้มเหลว

บันทึกข้อมูลเป็นธุรกรรม

เมื่อทำงานกับข้อมูลที่อาจเสียหายจากการแก้ไขที่เกิดขึ้นพร้อมกัน เช่น ตัวนับที่เพิ่มขึ้น คุณสามารถใช้การดำเนินการธุรกรรมได้ คุณให้คะแนนการดำเนินการนี้ Func การอัปเดต Func นี้จะใช้สถานะปัจจุบันของข้อมูลเป็นอาร์กิวเมนต์และแสดงผลสถานะใหม่ที่ต้องการซึ่งคุณต้องการเขียน หากไคลเอ็นต์อื่นเขียนไปยังตำแหน่งก่อนที่จะเขียนค่าใหม่สำเร็จ ระบบจะเรียกใช้ฟังก์ชันการอัปเดตอีกครั้งด้วยค่าปัจจุบันใหม่ และจะลองเขียนอีกครั้ง

ตัวอย่างเช่น ในเกม คุณอาจอนุญาตให้ผู้ใช้อัปเดตลีดเดอร์บอร์ดด้วยคะแนนสูงสุด 5 อันดับแรกได้ ดังนี้

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

    leaderBoardRef.RunTransaction(mutableData => {
      List<object> leaders = mutableData.Value as List<object>

      if (leaders == null) {
        leaders = new List<object>();
      } else if (mutableData.ChildrenCount >= MaxScores) {
        long minScore = long.MaxValue;
        object minVal = null;
        foreach (var child in leaders) {
          if (!(child is Dictionary<string, object>)) continue;
          long childScore = (long)
                      ((Dictionary<string, object>)child)["score"];
          if (childScore < minScore) {
            minScore = childScore;
            minVal = child;
          }
        }
        if (minScore > 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<string, object> newScoreMap =
                       new Dictionary<string, object>();
      newScoreMap["score"] = score;
      newScoreMap["email"] = email;
      leaders.Add(newScoreMap);
      mutableData.Value = leaders;
      return TransactionResult.Success(mutableData);
    });
}

การใช้ธุรกรรมจะป้องกันไม่ให้ลีดเดอร์บอร์ดไม่ถูกต้องหากผู้ใช้หลายคนบันทึกคะแนนพร้อมกันหรือไคลเอ็นต์มีข้อมูลที่ล้าสมัย หากธุรกรรมถูกปฏิเสธ เซิร์ฟเวอร์จะแสดงผลค่าปัจจุบันไปยังไคลเอ็นต์ ซึ่งจะเรียกใช้ธุรกรรมอีกครั้งด้วยค่าที่อัปเดต ซึ่งจะทำซ้ำจนกว่าธุรกรรมจะได้รับการยอมรับหรือมีการพยายามหลายครั้งเกินไป

เขียนข้อมูลแบบออฟไลน์

หากไคลเอ็นต์ขาดการเชื่อมต่อเครือข่าย แอปจะยังคงทำงานได้อย่างถูกต้อง

ไคลเอ็นต์ทุกรายที่เชื่อมต่อกับฐานข้อมูล Firebase จะเก็บรักษาเวอร์ชันภายในของข้อมูลที่ใช้งานอยู่ของตนเอง เมื่อมีการเขียนข้อมูล ระบบจะเขียนข้อมูลลงในเวอร์ชันในเครื่องนี้ก่อน จากนั้นไคลเอ็นต์ Firebase จะซิงค์ข้อมูลดังกล่าวกับเซิร์ฟเวอร์ฐานข้อมูลระยะไกลและไคลเอ็นต์อื่นๆ โดยใช้ "ความพยายามอย่างเต็มที่"

ด้วยเหตุนี้ การเขียนทั้งหมดลงในฐานข้อมูลจะทริกเกอร์เหตุการณ์ในเครื่องทันที ก่อนที่จะมีการเขียนข้อมูลลงในเซิร์ฟเวอร์ ซึ่งหมายความว่าแอปจะยังคงตอบสนองได้ไม่ว่าเวลาในการตอบสนองของเครือข่ายหรือการเชื่อมต่อจะเป็นอย่างไร

เมื่อการเชื่อมต่อกลับมาอีกครั้ง แอปของคุณจะได้รับชุดเหตุการณ์ที่เหมาะสมเพื่อให้ไคลเอ็นต์ซิงค์กับสถานะเซิร์ฟเวอร์ปัจจุบันโดยไม่ต้องเขียนโค้ดที่กำหนดเอง

ขั้นตอนถัดไป