facebook twitter hatena line google mixi email

「Unity/Firebase/CloudFunctions」の版間の差分

提供: 初心者エンジニアの簡易メモ
移動: 案内検索
(firebaseのサーバー側変更)
 
(同じ利用者による、間の44版が非表示)
行1: 行1:
 
==準備==
 
==準備==
 
[[Gcp/Firebase/CloudFunctions]] [ショートカット]
 
[[Gcp/Firebase/CloudFunctions]] [ショートカット]
 +
 +
==準備(dbを使う場合)==
 +
[[Gcp/Firebase/Firestore]]  [ショートカット]
  
 
==import==
 
==import==
行13: 行16:
  
 
==サンプル==
 
==サンプル==
ScoreEntry score = new ScoreEntry();
+
<pre>
AddEntry(score);
+
ScorePostInit score = new ScorePostInit();
 +
AddScore(score);
 +
GetTopScores();
 +
</pre>
 +
<pre>
 +
using System.Threading.Tasks;
 +
using System; // [Serializable]用
 +
public class ScorePostInit {
 +
    public string userName = "taro";
 +
    public int point = 100;
 +
    public string userId = "12341234";
 +
}
 +
async Task<object> AddScoreAsync(ScorePostInit score)
 +
{
 +
    object data = new Dictionary<object, object>
 +
    {
 +
        { "userName", score.userName },
 +
        { "point", score.point },
 +
        { "userId", score.userId },
 +
    };
 +
    return await functions.GetHttpsCallable("addScore").CallAsync(data)
 +
        .ContinueWith(task =>
 +
        {
 +
            return task.Result.Data;
 +
        });
 +
}
 +
public void AddScore(ScorePostInit score)
 +
{
 +
    AddScoreAsync(score).ContinueWith(task =>
 +
    {
 +
        if (task.IsFaulted)
 +
        {
 +
            // onError
 +
        }
 +
        else
 +
        {
 +
            // onComplete
 +
            Debug.Log("task.Result=" + task.Result); // OK
 +
        }
 +
    }, TaskScheduler.FromCurrentSynchronizationContext());
 +
}
 +
async Task<object> ReplaceScoreByUserIdAsync(ScorePostInit score, string userId)
 +
{
 +
    object data = new Dictionary<object, object>
 +
    {
 +
            { "userName", score.userName },
 +
            { "userId", userId },
 +
            { "point", score.point },
 +
    };
  
using System.Threading.Tasks;
+
    return await functions.GetHttpsCallable("replaceScore").CallAsync(data)
    class ScoreEntry {
+
         .ContinueWith(task =>
         public string Name = "taro";
+
        {
         public int Age = 100;
+
            return task.Result.Data;
    }
+
         });
     async Task<object> AddEntryAsync(ScoreEntry score)
+
}
 +
public void ReplaceScoreByUserId(ScorePostInit score, string userId)
 +
{
 +
     ReplaceScoreByUserIdAsync(score, userId).ContinueWith(task =>
 
     {
 
     {
         object data = new Dictionary<object, object>
+
         if (task.IsFaulted)
 
         {
 
         {
             { "name", score.Name },
+
             // onError
            { "age", score.Age },
+
        }
        };
+
        else
        return await functions.GetHttpsCallable("addEntry").CallAsync(data)
+
        {
            .ContinueWith(task =>
+
            // onComplete
            {
+
            Debug.Log("task.Result=" + task.Result); // OK
                return task.Result.Data;
+
        }
            });
+
    }, TaskScheduler.FromCurrentSynchronizationContext());
    }
+
}
    void AddEntry(ScoreEntry score)
+
[Serializable]
 +
class ResData
 +
{
 +
    public string status = "ok";
 +
    public string notice = "";
 +
    public List<ResUser> users;
 +
}
 +
[Serializable]
 +
class ResUser
 +
{
 +
    public int point = 0;
 +
    public string userName = "";
 +
    public string userId = "";
 +
}
 +
async Task<object> GetTopScoresAsync()
 +
{
 +
  object data = new Dictionary<object, object>
 +
  {
 +
        { "count", 3 },
 +
  };
 +
  return await functions.GetHttpsCallable("getTopScores").CallAsync(data)
 +
      .ContinueWith(task =>
 +
      {
 +
          return task.Result.Data;
 +
      });
 +
}
 +
public void GetTopScores()
 +
{
 +
    GetTopScoresAsync().ContinueWith(task =>
 
     {
 
     {
         AddEntryAsync(score).ContinueWith(task =>
+
         if (task.IsFaulted)
 
         {
 
         {
             if (task.IsFaulted)
+
             // onError
 +
        }
 +
        else
 +
        {
 +
            // onComplete
 +
            string json = task.Result.ToString();
 +
            Debug.Log("json=" + json); // {"status":"ok","notice":"","users":[{"point":140,"name":"siro"},{"point":130,"name":"saburo"},{"point":120,"name":"jiro"}]}
 +
            ResData resData = JsonUtility.FromJson<ResData>(json);
 +
            Debug.Log("status=" + resData.status);
 +
            Debug.Log("notice=" + resData.notice);
 +
            foreach (ResUser user in resData.users)
 
             {
 
             {
                 // onError
+
                 Debug.Log("user.name=" + user.userName);
 +
                Debug.Log("user.id=" + user.userId);
 +
                Debug.Log("user.point=" + user.point);
 
             }
 
             }
             else
+
        }
             {
+
    }, TaskScheduler.FromCurrentSynchronizationContext());
                // onComplete
+
}
             }
+
async Task<object> ReplaceUserNameByUserIdAsync(string userName, string userId)
         }, TaskScheduler.FromCurrentSynchronizationContext());
+
{
    }
+
    object data = new Dictionary<object, object>
 +
  {
 +
        { "name", userName },
 +
        { "id", userId },
 +
  };
 +
    return await functions.GetHttpsCallable("replaceUserNameByUserId").CallAsync(data)
 +
        .ContinueWith(task =>
 +
        {
 +
             return task.Result.Data;
 +
        });
 +
}
 +
public void ReplaceUserNameByUserId(string userName, string userId)
 +
{
 +
    ReplaceUserNameByUserIdAsync(userName, userId).ContinueWith(task =>
 +
    {
 +
        if (task.IsFaulted)
 +
        {
 +
             // onError
 +
        }
 +
        else
 +
        {
 +
            // onComplete
 +
             Debug.Log("task.Result=" + task.Result); // OK
 +
         }
 +
    }, TaskScheduler.FromCurrentSynchronizationContext());
 +
}
 +
</pre>
  
 
==firebaseのサーバー側変更==
 
==firebaseのサーバー側変更==
行56: 行176:
 
const admin = require('firebase-admin');
 
const admin = require('firebase-admin');
 
admin.initializeApp();
 
admin.initializeApp();
// 追加
+
// スコア追加
exports.addEntry = functions
+
exports.addScore = functions
 
   .region('asia-northeast1')
 
   .region('asia-northeast1')
 
   .https.onCall((data, context) =>
 
   .https.onCall((data, context) =>
 
{
 
{
   const entry = {
+
   const score = {
     name : data.name,
+
     userName : data.userName,
     point : data.point
+
     point : data.point,
 +
    userId : data.userId
 
   };
 
   };
   return admin.firestore().collection('entries')
+
   return admin.firestore().collection('scores')
     .add(entry)
+
     .add(score)
 
     .then((snapshot) =>
 
     .then((snapshot) =>
 
   {
 
   {
行72: 行193:
 
   });
 
   });
 
});
 
});
// 更新
+
// スコア更新
exports.updEntry = functions
+
exports.replaceScore = functions
 
   .region('asia-northeast1')
 
   .region('asia-northeast1')
 
   .https.onCall((data, context) =>
 
   .https.onCall((data, context) =>
 
{
 
{
   const entry = {
+
   const score = {
    name : data.name,
+
 
     point : data.point
 
     point : data.point
 
   };
 
   };
   return admin.firestore().collection('entries')
+
  if (data.userName != null) {
     .doc("ID1").set(entry)
+
    score.userName = data.userName;
 +
  }
 +
  if (data.userId != null) {
 +
    score.userId = data.userId;
 +
  }
 +
  console.log('data.userId=' + data.userId);
 +
   return admin.firestore().collection('scores')
 +
     .doc(data.userId).set(score)
 
     .then((snapshot) =>
 
     .then((snapshot) =>
 
   {
 
   {
行88: 行215:
 
   });
 
   });
 
});
 
});
 
+
//  トップcount件のScoreを取得
 
+
exports.getTopScores = functions
//  トップcount件のEntryを取得
+
exports.getTopEntries = functions
+
 
   .region('asia-northeast1')
 
   .region('asia-northeast1')
 
   .https.onCall((data, context) =>
 
   .https.onCall((data, context) =>
 
{
 
{
 
   const count = data.count;
 
   const count = data.count;
 
+
   return admin.firestore().collection('scores')
   return admin.firestore().collection('entries')
+
 
     .orderBy('point', 'desc')
 
     .orderBy('point', 'desc')
 
     .limit(count)
 
     .limit(count)
 
     .get()
 
     .get()
     .then((qSnapshot) =>
+
     .then((snapshot) =>
 
   {
 
   {
     return {
+
     var obj = {
      entries : qSnapshot.docs.map(x => x.data())
+
        status: 'ok',
     };
+
        notice: 'Processing succeeded.',
 +
        users: snapshot.docs.map(x => x.data()),
 +
     }
 +
    var json=JSON.stringify(obj);
 +
    return json;
 
   });
 
   });
 
});
 
});
 +
// ユーザ更新
 +
exports.replaceUserNameByUserId = functions.region('asia-northeast1').https.onCall((data, context) => {
 +
  const user = {
 +
    name : data.name,
 +
  };
 +
  if (data.id != null) {
 +
    user.id = data.id;
 +
  }
 +
  return admin.firestore().collection('users')
 +
    .doc(data.id).set(user)
 +
    .then((snapshot) =>
 +
  {
 +
    return 'OK';
 +
  });
 +
})
 
</pre>
 
</pre>
  

2019年8月8日 (木) 01:33時点における最新版

準備

Gcp/Firebase/CloudFunctions [ショートカット]

準備(dbを使う場合)

Gcp/Firebase/Firestore [ショートカット]

import

FirebaseFunctions.unitypackage をunityのAssets/ImportPackage/CustomPackageからImportする

初期化

using Firebase.Functions;
FirebaseFunctions functions = FirebaseFunctions.DefaultInstance;

東京リージョンの場合こちらを記述

using Firebase.Functions;
FirebaseFunctions functions = FirebaseFunctions.GetInstance("asia-northeast1");

サンプル

ScorePostInit score = new ScorePostInit();
AddScore(score);
GetTopScores();
using System.Threading.Tasks;
using System; // [Serializable]用
public class ScorePostInit {
    public string userName = "taro";
    public int point = 100;
    public string userId = "12341234";
}
async Task<object> AddScoreAsync(ScorePostInit score)
{
    object data = new Dictionary<object, object>
    {
        { "userName", score.userName },
        { "point", score.point },
        { "userId", score.userId },
    };
    return await functions.GetHttpsCallable("addScore").CallAsync(data)
        .ContinueWith(task =>
        {
            return task.Result.Data;
        });
}
public void AddScore(ScorePostInit score)
{
    AddScoreAsync(score).ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            // onError
        }
        else
        {
            // onComplete
            Debug.Log("task.Result=" + task.Result); // OK
        }
    }, TaskScheduler.FromCurrentSynchronizationContext());
}
async Task<object> ReplaceScoreByUserIdAsync(ScorePostInit score, string userId)
{
    object data = new Dictionary<object, object>
    {
            { "userName", score.userName },
            { "userId", userId },
            { "point", score.point },
    };

    return await functions.GetHttpsCallable("replaceScore").CallAsync(data)
        .ContinueWith(task =>
        {
            return task.Result.Data;
        });
}
public void ReplaceScoreByUserId(ScorePostInit score, string userId)
{
    ReplaceScoreByUserIdAsync(score, userId).ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            // onError
        }
        else
        {
            // onComplete
            Debug.Log("task.Result=" + task.Result); // OK
        }
    }, TaskScheduler.FromCurrentSynchronizationContext());
}
[Serializable]
class ResData
{
    public string status = "ok";
    public string notice = "";
    public List<ResUser> users;
}
[Serializable]
class ResUser
{
    public int point = 0;
    public string userName = "";
    public string userId = "";
}
async Task<object> GetTopScoresAsync()
{
   object data = new Dictionary<object, object>
   {
        { "count", 3 },
   };
   return await functions.GetHttpsCallable("getTopScores").CallAsync(data)
       .ContinueWith(task =>
      {
           return task.Result.Data;
      });
}
public void GetTopScores()
{
    GetTopScoresAsync().ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            // onError
        }
        else
        {
            // onComplete
            string json = task.Result.ToString();
            Debug.Log("json=" + json); // {"status":"ok","notice":"","users":[{"point":140,"name":"siro"},{"point":130,"name":"saburo"},{"point":120,"name":"jiro"}]}
            ResData resData = JsonUtility.FromJson<ResData>(json);
            Debug.Log("status=" + resData.status);
            Debug.Log("notice=" + resData.notice);
            foreach (ResUser user in resData.users)
            {
                Debug.Log("user.name=" + user.userName);
                Debug.Log("user.id=" + user.userId);
                Debug.Log("user.point=" + user.point);
            }
        }
    }, TaskScheduler.FromCurrentSynchronizationContext());
}
async Task<object> ReplaceUserNameByUserIdAsync(string userName, string userId)
{
    object data = new Dictionary<object, object>
   {
        { "name", userName },
        { "id", userId },
   };
    return await functions.GetHttpsCallable("replaceUserNameByUserId").CallAsync(data)
        .ContinueWith(task =>
        {
            return task.Result.Data;
        });
}
public void ReplaceUserNameByUserId(string userName, string userId)
{
    ReplaceUserNameByUserIdAsync(userName, userId).ContinueWith(task =>
    {
        if (task.IsFaulted)
        {
            // onError
        }
        else
        {
            // onComplete
            Debug.Log("task.Result=" + task.Result); // OK
        }
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

firebaseのサーバー側変更

functions.https.onCall を使ってシリアル&認証トークンで接続するようにする

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
// スコア追加
exports.addScore = functions
  .region('asia-northeast1')
  .https.onCall((data, context) =>
{
  const score = {
    userName : data.userName,
    point : data.point,
    userId : data.userId
  };
  return admin.firestore().collection('scores')
    .add(score)
    .then((snapshot) =>
  {
    return 'OK';
  });
});
// スコア更新
exports.replaceScore = functions
  .region('asia-northeast1')
  .https.onCall((data, context) =>
{
  const score = {
    point : data.point
  };
  if (data.userName != null) {
     score.userName = data.userName;
  }
  if (data.userId != null) {
    score.userId = data.userId;
  }
  console.log('data.userId=' + data.userId);
  return admin.firestore().collection('scores')
    .doc(data.userId).set(score)
    .then((snapshot) =>
  {
    return 'OK';
  });
});
//  トップcount件のScoreを取得
exports.getTopScores = functions
  .region('asia-northeast1')
  .https.onCall((data, context) =>
{
  const count = data.count;
  return admin.firestore().collection('scores')
    .orderBy('point', 'desc')
    .limit(count)
    .get()
    .then((snapshot) =>
  {
    var obj = {
        status: 'ok',
        notice: 'Processing succeeded.',
        users: snapshot.docs.map(x => x.data()),
    }
    var json=JSON.stringify(obj);
    return json;
  });
});
// ユーザ更新
exports.replaceUserNameByUserId = functions.region('asia-northeast1').https.onCall((data, context) => {
  const user = {
    name : data.name,
  };
  if (data.id != null) {
    user.id = data.id;
  }
  return admin.firestore().collection('users')
    .doc(data.id).set(user)
    .then((snapshot) =>
  {
    return 'OK';
  });
})

参考

https://firebase.google.com/docs/functions/callable?hl=ja

https://devlog.hassaku.blue/2019/03/unity-firebase-firebase.html

https://firebase.google.com/docs/reference/unity/class/firebase/functions/firebase-functions

https://github.com/firebase/quickstart-unity/blob/master/functions/testapp/Assets/Firebase/Sample/Functions/UIHandler.cs