Quantcast
Channel: Technology – ZEBU
Viewing all articles
Browse latest Browse all 5

Shift Painlessly to Firebase from Parse in Unity

0
0

I have been using Parse for more than a year now, mainly to collect player feedback and for server side configuration. But with Facebook deciding to discontinue Parse, I had to switch to another backend with minimal changes to game code. Google had been touting Firebase as a capable mobile backend for some time (they sounded quite passionate about Firebase at Google For Mobile conference in 2015). After studying the API provided by their Unity plugin, I decided to give it a go.

Note that at the time of writing, the Unity Plugin is yet to support slew of features that the revamped Firebase SDK has, like remote configuration, content download and notifications. I’ll update this post once that happens.

I attempted two things with Firebase:

  • Upload player feedback to the cloud
  • Configure in-game advertisement frequency remotely

It was a hit-and-miss approach but I managed to get the system working and is now part of my next game, Unmind. Here are the steps that I followed and you can too, for a painless shift to Firebase:

1. Install the Firebase Unity Plugin

Download firebase.unitypackage from https://github.com/firebase/Firebase-Unity and import into your project. The plugin works in the editor also but it has some dependencies on the Java runtime so I skipped that part:

Screen Shot

2. Set up the project in Firebase console

Sign-in into https://firebase.google.com/ from your Google account. Then go to https://console.firebase.google.com/ and create a New Project:

Screen Shot 2016-05-27 at 12.37.43 PM

Enable anonymous user authentication under the Auth->Sign-in Method to allow the game to access the database anonymously like Parse.

Screen Shot 2016-05-27 at 12.50.23 PM

3. Set up the plugin interface

I use the manager-provider pattern for most plugins including Parse. All backend calls are made through the provider interface so that I can swap out the backend very easily.

DataManager.cs:

using UnityEngine;
using System.Collections;
 
public class DataManager : MonoBehaviour {
 
 public static DataManager instance;
 public CloudDataProvider cloudDataProvider;
 
   void Awake () {
     if(instance == null){
       DontDestroyOnLoad(gameObject);
       instance = this; 
       init ();
     }else if( instance != this ){
       Destroy(gameObject);
     }
   }
 
   void init(){
     cloudDataProvider = GetComponent<CloudDataProvider> ();
     cloudDataProvider.load ();
   }
 }

The feedback interface can change based on requirement. In my case I collect the rating and email and feedback from the user:

CloudDataProvider.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class CloudDataProvider : MonoBehaviour {

  public void load(){
  }

  public void uploadFeedback(int rating, string comment, string email){
  }

}

4. Log in Anonymously

I prefer anonymous login like most other developers. For feedback collection and remote config this login method should suffice. Add the following code:

CloudDataProvider.cs

…

 public delegate void PrepareCallback();
 public PrepareCallback onPrepared;

 IFirebase firebase;

 public void load(){
   firebase = Firebase.CreateNew (FIREBASE_URL);

   if (firebase != null) {

     firebase.AuthAnonymously ((AuthData auth) => {
       Debug.Log ("auth success!!" + auth.Uid);

       if(onPrepared != null)
         onPrepared();

     } , (FirebaseError error) => {
       Debug.Log ("auth failure!!");
     } );

   }
}

…

Replace FIREBASE_URL with the one mentioned at the top of the Database section in the Firebase console. Note the onPrepared delegate. This is used to make config fetch calls after a successful login.

5. Implement Feedback Upload

Firebase stores data as a JSON tree. We will store the player feedback under the ‘feedback’ branch. To create a new entry automatically for each feedback use the Push method:

 …
 public void uploadFeedback(int rating, string comment, string email){
   if (firebase != null) {
     IFirebase feedback = firebase.Child ("feedback");

     Dictionary<string, object> data = new Dictionary<string, object> ();
     data ["rating"] = rating;
     data ["comment"] = comment;
     if(!System.String.IsNullOrEmpty(email))
       data ["email"] = email;

     //Add game specific data like level number here

     data ["deviceId"] = SystemInfo.deviceUniqueIdentifier;
     data ["os"] = SystemInfo.operatingSystem;
     data ["time"] = System.DateTime.Now.ToString ("yyyy-MM-dd-HH-mm-ss-zzz");

     if(feedback != null)
       feedback.Push ().SetValue (data);
   }
 }
 …

The data has to be written in to a [string, object] map which supports float, int, bool, string and another map as values. If authentication failed due to any reason the firebase instance will be null, hence the null check. Call this function as needed:

DataManager.instance.cloudDataProvider.uploadFeedback(rating, feedbackText.text, emailText.text);

Once called you should be able to see an entry under the database section of the Firebase console:

Screen Shot 2016-05-27 at 3.26.32 PM

For every Push call Firebase will create a new entry under the ‘feedback’ branch (set to a random value)

6. Load Config Data from Firebase

To read configuration data make an entry first in the database:

Screen Shot 2016-05-27 at 3.32.53 PM

Here I have created an entry for ‘adInterval’ under the config branch. [NOTE: The latest Firebase SDK has a separate system for remote config which is currently not supported by the Unity plugin]. Add the following code to read a float:

 …
 public void getConfigDataFloat(string key, System.Action<float> cb){
   if (firebase != null) {
     IFirebase config = firebase.Child (CONFIG_PATH).Child (key);
     config.ValueUpdated += (object sender, FirebaseChangedEventArgs e) => {
       if(cb != null && e != null && e.DataSnapshot != null)
         cb.Invoke(e.DataSnapshot.FloatValue);

       cb = null;
     } ;
   }
 }
 …

Here CONFIG_PATH is set to ‘config’. The ValueUpdated callback will get called first time it is set and also every time data is changed in the console. However, in the code above the IFirebase instance config will get destroyed so it won’t happen. Make it a member variable if you want this functionality.

Now we can use the onPrepared delegate to read the desired data. In my case I read the ‘adInterval’ value from the AdManager class:

 …
 void Start(){
   DataManager.instance.cloudDataProvider.onPrepared += onCloudPrepared;
 }
 …

 void onCloudPrepared(){
   DataManager.instance.cloudDataProvider.getConfigDataFloat (KEY_AD_INTERVAL, (float value) => {
     Debug.Log("Ad Interval = " + value);
     if(value > 0 && value <= maxAdInterval)
       PlayerPrefs.SetFloat(KEY_AD_INTERVAL, value);
   } );
 }
 …

Note that this class is also singleton. If you need to make the call from a scene specific object then you need to use a weak reference to prevent memory leaks. An alternative would be to read the values locally into member variables of CloudDataProvider itself [Store them in PlayerPrefs to support caching if you want]

7. Making it Secure

By default the firebase database has no read/write restrictions. Anybody can read/write into it which could spell disaster for your game. We want to allow only authenticated users access into the system. Firebase lets you define read/write access for each branch in the database via rules. These rules are applied every time the database is accessed.

Rules can get pretty complicated. In my case I wanted only authenticated users to be able to write into the feedback branch and read from the config branch. To set rules go into the ‘Rules’ tab of the database section and make the following changes:

{
    "rules": {
        ".read": false,
        ".write": false,
     
        "config" : {
          ".read" : "auth !== null"
        },

        "feedback" : {
          ".write" : "auth !== null"
        },
    }
}

When we get “.read”: true for a branch then all elements within that branch are readable. Same happens for “.write”:true. The auth variable is a system variable that becomes non-null only when a user is authenticated. So now we have read access to ‘config’ and write access to ‘feedback’ for authenticated users. Feel free to play around with these rules. You can add all sorts of checks to allow data write in very specific formats. For more details refer https://firebase.google.com/docs/database/security/

That should be it. At least it was for me! If you get stuck anywhere you can ask me questions in the comments section below.


Viewing all articles
Browse latest Browse all 5

Latest Images

Trending Articles





Latest Images