「Unity/Firebase/CloudFunctions」の版間の差分
提供: 初心者エンジニアの簡易メモ
(→参考) |
(→"The type initializer for 'Firebase.FirebaseApp' threw an exception."エラー) |
||
(同じ利用者による、間の55版が非表示) | |||
行1: | 行1: | ||
==準備== | ==準備== | ||
[[Gcp/Firebase/CloudFunctions]] [ショートカット] | [[Gcp/Firebase/CloudFunctions]] [ショートカット] | ||
+ | |||
+ | ==準備(dbを使う場合)== | ||
+ | [[Gcp/Firebase/Firestore]] [ショートカット] | ||
==import== | ==import== | ||
行6: | 行9: | ||
==初期化== | ==初期化== | ||
− | + | using Firebase.Functions; | |
− | + | FirebaseFunctions functions = FirebaseFunctions.DefaultInstance; | |
− | + | 東京リージョンの場合こちらを記述 | |
+ | using Firebase.Functions; | ||
FirebaseFunctions functions = FirebaseFunctions.GetInstance("asia-northeast1"); | FirebaseFunctions functions = FirebaseFunctions.GetInstance("asia-northeast1"); | ||
+ | |||
+ | ==サンプル== | ||
+ | <pre> | ||
+ | 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 }, | ||
+ | }; | ||
+ | |||
+ | 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()); | ||
+ | } | ||
+ | </pre> | ||
==firebaseのサーバー側変更== | ==firebaseのサーバー側変更== | ||
行18: | 行176: | ||
const admin = require('firebase-admin'); | const admin = require('firebase-admin'); | ||
admin.initializeApp(); | admin.initializeApp(); | ||
− | exports. | + | // スコア追加 |
+ | exports.addScore = functions | ||
.region('asia-northeast1') | .region('asia-northeast1') | ||
.https.onCall((data, context) => | .https.onCall((data, context) => | ||
{ | { | ||
− | const | + | 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 | point : data.point | ||
}; | }; | ||
− | return admin.firestore().collection(' | + | 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) => | .then((snapshot) => | ||
{ | { | ||
行33: | 行215: | ||
}); | }); | ||
}); | }); | ||
− | + | // トップcount件のScoreを取得 | |
− | // | + | exports.getTopScores = functions |
− | exports. | + | |
.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(' | + | |
.orderBy('point', 'desc') | .orderBy('point', 'desc') | ||
.limit(count) | .limit(count) | ||
.get() | .get() | ||
− | .then(( | + | .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'; | ||
+ | }); | ||
+ | }) | ||
</pre> | </pre> | ||
+ | |||
+ | =="The type initializer for 'Firebase.FirebaseApp' threw an exception."エラー == | ||
+ | 2018.4.8f1から2019.2.12f1に更新すると以下エラーが起こったが、functionsを最新にすると治った。 | ||
+ | Rethrow as TypeInitializationException: The type initializer for 'Firebase.FirebaseApp' threw an exception. | ||
==参考== | ==参考== | ||
行60: | 行263: | ||
https://firebase.google.com/docs/reference/unity/class/firebase/functions/firebase-functions | 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 |
2020年2月6日 (木) 21:04時点における最新版
目次
準備
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'; }); })
"The type initializer for 'Firebase.FirebaseApp' threw an exception."エラー
2018.4.8f1から2019.2.12f1に更新すると以下エラーが起こったが、functionsを最新にすると治った。
Rethrow as TypeInitializationException: The type initializer for 'Firebase.FirebaseApp' threw an exception.
参考
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