👛

【無料枠あり】DBのバックエンドをC#で操作する方法【Unity】

2020/11/28に公開

Unity で、「サーバーにデータを保存する」という場合、何かしらのサーバーサービスを使用する必要があります。

しかし、サーバーサービスをは有料なものが多く、なかなか手を出しにくいのも事実です。

さらに、無料枠で使用できるものもありますが、URL を叩く形式の API であることも多く、さらに難易度が上がってしまいます。

こういったこともあり、「難しそうだな」と諦めてしまった人も多いのではないでしょうか?

「何かいいサービスはないか」

そう考えていろいろと調べていくと、「無料枠があり、C# で記述できる」というサービスを見つけることができました。

ということで本記事では、Unity のバックエンドとしてDB管理する処理を、C# でいろいろ操作する方法について解説します。

DBのバックエンドをC#で操作する方法

結論をいうと、Backendlessというサービスを使用します。

https://backendless.com/

サービス側の準備

まずはアカウント登録からしていきましょう。

右上の「Sign Up」から新規登録ができます。

新規登録をするとメール認証があるので、届いたメールのリンクをクリックして、認証を済ませましょう。

認証できたら、プロジェクトの新規作成ができるようになります。

App Name は適当で構いません。
その下は Blank App を選択しました。

入力と選択ができたら、右下の「Create」ボタンを押しましょう。

すると管理画面が表示されるので、「Manage > App Settings > API KEYS」と進んで、次のふたつのキーをコピーしておきましょう。

  • Application ID
  • .NET API key

これで、Backendless 側の準備は完了です。

Unity側の準備

続いて、Unity 側の準備です。

unitypackage のインポート

最新のBackendlessのunitypackageをダウンロードしてください。

https://github.com/Backendless/.NET-SDK/tree/master/Projects/BackendlessUnitySDK/UnityPackage

unitypackage については公式ドキュメントに載っておらず、公式のフォーラムから探してきました(探すの大変でした...)。

執筆日時点では「6.0.1」が最新でした。

Unity にインポートしましょう。

しかし、ここでトラップがあり、BackendlessSDK フォルダを全部インポートするとビルドエラーになってしまいます。

インポート後に不要なものを削除するか、事前に必要なものだけインポートするかの対応が必要です。

試行錯誤の結果、以下のものだけで十分でした(8割くらい削除)。

これでエラーがなくなるはずです。

APIキーの設定

次に、先ほどコピーした API キーを設定していきます。

unitypackage をインポートすると、「TestDataService」という動作確認用のシーンが入っているので、これを開いてください。

「BackendlessPlugin」を選択すると、DevelopmentAppID と DevelopmentApiKey を設定するところがあるので、ここにコピーしたものを入力しましょう。

これでUnity 側の準備も完了です。

動かすプログラムの確認

実際に動かすプログラムTestDataService.csを見てみましょう。

TestDataService.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using BackendlessAPI;
using BackendlessAPI.Data;
using BackendlessAPI.Async;
using BackendlessAPI.Persistence;
using UnityEditor;

public class TestDataService : MonoBehaviour
{

    void Start()
    {
#if UNITY_EDITOR
        EditorApplication.playModeStateChanged += HandleOnPlayModeChanged;
#endif
        SaveTestData();
    }

    void SaveTestData()
    {
        Dictionary<string, object> dict = new Dictionary<string, object>();
        dict.Add("nameTimeColumn", "Bob Jones " + System.DateTime.UtcNow);

        Debug.Log("Saving test data...");

        Backendless.Data.Of("TestTable")
            .Save(dict,
                new AsyncCallback<Dictionary<string, object>>(
                    response =>
                    {
                        Debug.Log("Test data saved with objectId " + response["objectId"]);

                        GetTestData();
                    },
                    error =>
                    {
                        Debug.LogError("Failed to save test data: Code = " + error.FaultCode
                            + ", Message = " + error.Message
                            + ", Details = " + error.Detail);
                    }
                )
           );

        Debug.Log("Adding RT listener...");
        Backendless.Data.Of("TestTable").RT().AddCreateListener((obj) =>
      {
          Debug.Log("RT: Object created ");
          Debug.Log("RT object - " + obj);
      });
    }

    void GetTestData()
    {
        DataQueryBuilder query = DataQueryBuilder.Create();
        query.SetWhereClause("nameTimeColumn LIKE '%Bob%'");
        query.AddSortBy("created desc");
        Backendless.Data.Of("TestTable")
            .Find(query,
                new AsyncCallback<IList<Dictionary<string, object>>>(
                    response =>
                    {
                        Debug.Log("Found " + response.Count + " test data results. First object ID is " + response[0]["objectId"]);
                    },
                    error =>
                    {
                        Debug.Log("Failed to find test data: Code = " + error.FaultCode
                                + ", Message = " + error.Message
                                + ", Details = " + error.Detail);
                    }
                )
            );
    }

#if UNITY_EDITOR
    void HandleOnPlayModeChanged(PlayModeStateChange stateChange)
    {
        // This method is run whenever the playmode state is changed.
        if (stateChange == PlayModeStateChange.ExitingEditMode || stateChange == PlayModeStateChange.ExitingPlayMode)
        {
            Backendless.RT.Disconnect();
        }
    }
#endif
}

ちょっと長いですが、やっていることは簡単です。

  1. レコードを新規追加する
  2. 追加したレコードを取得する

レコードの新規追加時に用意しているデータは、nameTimeColumnというキーに対して、値は"Bob Jones "の文字列に時刻をくっつけていますね。

Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("nameTimeColumn", "Bob Jones " + System.DateTime.UtcNow);

これが、「列の情報」ということです。

続いて、新規登録のところを見てみましょう。

新規登録
Backendless.Data.Of("TestTable")
    .Save(dict,
        new AsyncCallback<Dictionary<string, object>>(
            response =>
            {
                Debug.Log("Test data saved with objectId " + response["objectId"]);

                GetTestData();
            },
            error =>
            {
                Debug.LogError("Failed to save test data: Code = " + error.FaultCode
                    + ", Message = " + error.Message
                    + ", Details = " + error.Detail);
            }
        )
    );

TestTableというのは、登録する先のテーブル名です。

SaveメソッドにDictionary を渡して登録をしていますね。

成功したか失敗したかをコールバックで受け取ることができます。

また、取得についてはFindメソッドを使用します。

データ取得
void GetTestData()
{
    DataQueryBuilder query = DataQueryBuilder.Create();
    query.SetWhereClause("nameTimeColumn LIKE '%Bob%'");
    query.AddSortBy("created desc");
    Backendless.Data.Of("TestTable")
        .Find(query,
            new AsyncCallback<IList<Dictionary<string, object>>>(
                response =>
                {
                    Debug.Log("Found " + response.Count + " test data results. First object ID is " + response[0]["objectId"]);
                },
                error =>
                {
                    Debug.Log("Failed to find test data: Code = " + error.FaultCode
                            + ", Message = " + error.Message
                            + ", Details = " + error.Detail);
                }
            )
        );
}

DataQueryBuilderの中身をいろいろと設定することで、絞り込みや並び替えをすることができます。

あとは、取得したいテーブル「TestTable」を指定するだけです。

動作確認

実際に動かしてみると、次のログが出てきて、実行に成功したことがわかります。

データが登録されているのかを確認してみましょう。

「Data」を選ぶと、作成した「TestTable」が表示されていますね。

さらに、レコードが追加されていることも確認できます。

今回は登録が成功したあと、すぐにデータ取得処理をしたので、遅延なく即時更新がされていることも確認できましたね。

よくある質問

ここで、よくある質問をまとめておきます。

コールバックを使うと体調が悪くなります

あなたの風邪が「コールバック」からくる人は、非同期メソッドがおすすめです。
SaveAsyncを使用すれば、長々とコールバックを書かなくてよくなります。

非同期のサンプル
async void SaveTestDataAsync()
{
    var dict = new Dictionary<string, object>
    {
        { "nameTimeColumn", "Bob Jones " + System.DateTime.UtcNow }
    };

    var result = await Backendless.Data.Of("TestTable").SaveAsync(dict);
}

Dictionary を使うと体調が悪くなります

あなたの風邪が「Dictionary」からくる人は、独自クラスの定義がおすすめです。
非同期を組み合わせれば、さらにスッキリしますね。

独自クラスのサンプル
async void SaveEntityDataAsync()
{
    var entity = new TestTable
    {
        nameTimeColumn = "Bob Jones " + System.DateTime.UtcNow
    };

    var result = await Backendless.Data.Of<TestTable>().SaveAsync(entity);
}
public class TestTable
{
    public string nameTimeColumn { get; set; }
}

無料枠の注意点

料金プランは次のようになっています。

しかし、この料金プランではひとつ注意があります。

それは何かというと、新規アカウント作成したあとは、月額99ドルが1ヶ月無料のプランに入ってしまう点です。

なので、何もしないと1ヶ月後に99ドルの請求がきてしまうことになるので、注意が必要です。

それを避けるには、無料プランに移行する必要があるのですが、方法はふたつあります。

  • ミッションをクリアする
  • 50ドル払う

ミッションとは、Backendless の内容が学べるオンラインの講習です。

画像のように、ゲーム感覚でミッションをクリアして、ポイントを集めることができます。

集めたポイントが 15,000 を超えると、無料アカウントのロックが解除できるようです。

私もまだやっていないので、どのくらい時間がかかるのかはわかりません。

「その時間がもったいない」という人は、お金で解決もできるようになっています。

最後に

Backendless の基本的な使い方について解説しました。

今回は導入部分だけでしたが、Backendless はたくさんの機能を提供されているので、もっといろんなことができます。

もし興味が出てきた人は、公式ドキュメントを参照しながら触ってみてください。

おまけとして、Unity のバックエンド開発には、PlayFab も最適です。

もしよければ、私が運営している PlayFab 専用のブログもご覧になってください。

追記:本を書きました

無料プランを開放するまでの全手順をまとめた本を書きました。
https://booth.pm/ja/items/2582368

私は8時間かかったのですが、本を読むことで2時間に短縮できると思います。
気になる方はチェックしてみてください。

Discussion