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

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

คุณต้องดำเนินการต่อไปนี้ก่อนจึงจะใช้ 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&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 พร้อมกัน จากนั้นจะใช้คีย์เพื่อสร้างรายการที่ 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 =&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 จะซิงค์ข้อมูลดังกล่าวกับเซิร์ฟเวอร์ฐานข้อมูลระยะไกลและกับไคลเอ็นต์อื่นๆ โดยอิงตาม "ความพยายามที่ดีที่สุด"

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

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

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