sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

Unity Android/Windows 環境における SQLite の使い方の基本

この記事は、Unity で SQLite を利用するために Standalone/Android 端末で SQLite DB ファイルを利用できる環境構築をまとめたものです。

f:id:shikaku_sh:20200206100844p:plain
SQLite

開発環境、テスト環境

開発環境

テスト環境

iOS は手元に環境がないため、以下で説明を省略していますが、考え方は同じです。

環境準備

最初に SQLite プラグイン ファイルと DB の編集用ツールを準備します。

SQLite プラグイン ファイルの説明

プラグイン ファイルの準備は、2つのポイントがあります。

まず、1つ目。現在は端末ごとに 32/64 ビットの両方に対応させる必要があります。

Android の64 ビット対応の基本的な設定について、認識がないかたは「64 ビット対応の記事」も参考に。

2つ目。Unity の期待するプラグイン ファイルの形式は端末によって異なることがあります。WindowsDLL 形式で、Android (Linux 1) のプラグイン形式は so か aar です。

そんなわけなので、端末ごとに別々のプラグイン ファイルを用意して設定することになります。(詳細は Unity マニュアル「Building plug-ins for desktop platforms」に詳しいです)

Windows/Android、そして、32/64 ビットの違いで別のプラグインを用意するわけなので、合計4種類のプラグイン ファイルが必要そうです……が、AndroidAAR (Android ARchive) 形式のプラグイン ファイルの利用が現在は Unity で推奨されています。2

この AAR 形式は、32/64 ビットの違いや ARM アーキテクチャ 3 かどうかの違いを1ファイルでまとめて対応、設定できます。便利ですね。

一旦まとめましょう。

Windows 32/64 用の DLL ファイルに対応するのと、Android は AAR ファイル1つにおまかせ対応します。なので、合計3種類のプラグイン ファイルを準備・設定することになります。

補足すると Unity は ARM ではない 64 ビットに非対応、かつ、ARM ではない 32 ビット対応も Unity 2019.3 で完全に終了しているようです。 4

f:id:shikaku_sh:20200206101517p:plain:w500
x64 は設定しようとしても、こんな警告がでました

プラグイン ファイルのダウンロード

ダウンロードは、以下の SQLite 公式の SQLite.org からダウンロードします。(XXXXXXX はバージョンの番号を示します)

Android

Windows

Windows 版は同じファイル名なので、解凍後はファイルが混ざらないように注意。

f:id:shikaku_sh:20200206101122p:plain:h500
2020年2月だと次の3つ

DB ブラウザーのダウンロード

SQLite のテーブル設計や確認は、別途ブラウザーを用意しておいたほうが便利です。なにか使い慣れたものを用意しておこう。

有償でもよければ navicat がオススメ。(RDBMS の商用ツール定番だと思う)非商用だと 12,000 円。

プラグイン ファイルのインポート(フォルダー設定)

入手したプラグイン ファイル(DLL/AAR)を Unity のプロジェクト内の「Assets」にインポートしていきます。

注意点があって、好き勝手にプラグイン ファイルを「Assets」フォルダー内にインポートすればいいわけではないです。

プラグイン ファイルは、次のように決まったフォルダーパスを作成し、それぞれに対応するプラグイン ファイルをインポートする必要があります

プラットフォーム CPU パス 補足
Android - Assets\Plugins\Android まとめて AAR ファイルを設定
Windows x86 Assets\Plugins\x86 x86 DLL を設定
Windows x64 Assets\Plugins\x86_64 x64 DLL を設定

f:id:shikaku_sh:20200206102023p:plain
Android のルールなのだ

一応、詳細は Unity マニュアル「Plugin インスペクター」などにまとめられています。

古い設定方法(.so 形式のインポート)は、有償の Unity Assets Store のツールを開き、「Package Content」などは実例として参考になります。

本記事の一番最後にも、同様の .so 形式のファイルを直接インポートして設定したリポジトリーを公開しておきます(動作確認済)ただし、.so 形式のファイル作成自体が Windows 環境だと困難+Unity 推奨は AAR 形式みたいです。留意ください。

プラグイン ファイルのインポート

プラグイン ファイルのファイル名は、次のようにします。(XXXXXXX の部分はバージョン)

この名称変更で「libsqliteX」にするのは、必須です。どういうことかというと、Android 版のプラグイン ファイルの名前と一致させています。

f:id:shikaku_sh:20200206102626p:plain:w400
Android
f:id:shikaku_sh:20200206102634p:plain:w400
Windows x86
f:id:shikaku_sh:20200206102653p:plain:w400
Windows x64

補足として、一致させている AAR 形式のファイルは次のものです。

  • sqlite-android-XXXXXXX.aar の拡張子を zip に変更、解凍
  • jni フォルダー内に libsqliteX.so ファイルが存在

Android 版 インスペクター設定

インスペクターが Android にチェックをつけているかどうか確認します。他の項目 EditorStandalone は使用しないため、チェックをつけませんでした。

f:id:shikaku_sh:20200206103627p:plain
Android

Windows 版 インスペクター設定

インスペクターは Any Platform の選択を外して、Standalone にチェックをつけます。私の開発環境は Windows 10 なので Editor にもチェックをつけました。

Platform settingx86/x64 をそれぞれ設定します。これで、インスペクターの設定はこれだけで完了です。

f:id:shikaku_sh:20200206103652g:plain:w600
ミスのないように!

環境ごとに、どのプラグイン ファイルを利用するのか設定したわけですね。

スクリプトの準備(SQLiteUnityKit)

SQLite を利用するためのスクリプト部分は、フリーの定番らしい SQLiteUnityKit を使ってテストすることにします。

SQLiteUnityKit は、マルチバイト文字(日本語)非対応のため注意。(別記事で、修正方法を掲載予定)

zip ダウンロード or リポジトリーのクローン、どちらでもいいと思います。

ダウンロードできたら、次の2つのファイルを取り出してください。Unity のプロジェクトに Assets\Scripts フォルダーなどを作成して、次の2ファイルをインポートします。

  • DataTable.cs
  • SqliteDatabase.cs

インポートすると SqliteDatabase.cs ファイルは、WWW クラスをしているコードの部分は UnityWebRequest クラスを使ってほしい旨の注意が表示されていました。

とりあえず重篤な問題ではないので、このままで続行します。

f:id:shikaku_sh:20200206103938p:plain:w400
コードが古くなっているみたいです

Sqlitedatabase.cs の変更箇所

DLL から関数をインポートしている部分を次のように変更します。

    [DllImport("libsqliteX", EntryPoint = "sqlite3_open")]
    private static extern int sqlite3_open(string filename, out IntPtr db);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_close")]
    private static extern int sqlite3_close(IntPtr db);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_prepare_v2")]
    private static extern int sqlite3_prepare_v2(IntPtr db, string zSql, int nByte, out IntPtr ppStmpt, IntPtr pzTail);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_step")]
    private static extern int sqlite3_step(IntPtr stmHandle);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_finalize")]
    private static extern int sqlite3_finalize(IntPtr stmHandle);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_errmsg")]
    private static extern IntPtr sqlite3_errmsg(IntPtr db);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_column_count")]
    private static extern int sqlite3_column_count(IntPtr stmHandle);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_column_name")]
    private static extern IntPtr sqlite3_column_name(IntPtr stmHandle, int iCol);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_column_type")]
    private static extern int sqlite3_column_type(IntPtr stmHandle, int iCol);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_column_int")]
    private static extern int sqlite3_column_int(IntPtr stmHandle, int iCol);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_column_text")]
    private static extern IntPtr sqlite3_column_text(IntPtr stmHandle, int iCol);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_column_double")]
    private static extern double sqlite3_column_double(IntPtr stmHandle, int iCol);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_column_blob")]
    private static extern IntPtr sqlite3_column_blob(IntPtr stmHandle, int iCol);

    [DllImport("libsqliteX", EntryPoint = "sqlite3_column_bytes")]
    private static extern int sqlite3_column_bytes(IntPtr stmHandle, int iCol);

DllImport("sqlite3", EntryP... となっていた部分の sqlite3libsqliteX に変更しました。これは、プラグイン ファイルをインポートしたときのファイル名と一致させています

Android 版 AAR ファイルを変更するよりも、dll のファイル名とスクリプトの宣言部分を変更して対応しました。(AAR ファイルも解説したとおり zip ファイルですが、再圧縮するくらいなら、こっちのほうが無難に思いました)

サンプルの DB ファイルの用意

SQLite の DB ファイルを作成します。 ここでは、こんなテーブルとデータを作成しました。

f:id:shikaku_sh:20200206104120p:plain:w400
2つだけデータのある charactor テーブル

作成した DB ファイルは Assets\StreamingAssets フォルダーを作成してインポートします。作成した StreamingAssets も特別な意味をもつフォルダーになります。

ざっくり説明すると、StreamingAssets フォルダーにインポートしたファイルは、ビルド先のプラットフォームでは、そのまま何も変換されない状態で展開されます。(ファイル圧縮が無い)

注意として StreamingAssets フォルダーは readonly です。なので、StreamingAssets フォルダーに DB ファイルがあっても insert も update もできません。そのため、persistentDataPath(アプリのインストール先のフォルダ)に DB ファイルをコピーする、といった手法が一般的になっているようです。

DB ファイルをコピーする操作は、SQLiteUnityKit の SqliteDatabase のコンストラクターが担当しています。(WWW クラスを利用して警告がでていた部分です)

f:id:shikaku_sh:20200206104327p:plain
こういうファイル名は小文字だけがいいと思います

テストコード

DB のデータを画面に表示する UI.Text に追加したスクリプトは次です。Owner Object は自分自身 Text を設定します。

f:id:shikaku_sh:20200206104620p:plain:w500
Text にデータを表示させたい

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class Test : MonoBehaviour
{
    public Text OwnerObject;
    private List<string> Charactors = new List<string>();

    // Start is called before the first frame update
    void Start()
    {
        try
        {
            var fileName = "sample.db";
            var db = new SqliteDatabase(fileName);
            var query = db.ExecuteQuery("SELECT * FROM charactors");

            foreach (var row in query.Rows)
            {
                var id = row["id"];
                var name = row["name"];
                var data1 = row["data1"];
                var data2 = row["data2"];

                var text = $"ID:{id}, Name:{name}, DATA1:{data1}, DATA2:{data2}";

                Charactors.Add(text);
            }

        } 
        catch (Exception ex)
        {
            Charactors.Add(ex.Message);
        }
    }

    // Update is called once per frame
    void Update()
    {
        if (OwnerObject != null && Charactors.Count > 0)
        {
            string text = "";
            
            if (Charactors.Count > 0)
            {
                text = string.Join("\r\n", Charactors);
            }
            else
            {
                text = "キャラクターが存在しません";
            }

            OwnerObject.text = text;
        }
    }
}

実行結果

こんな感じになります。がはは、グッドだ!

f:id:shikaku_sh:20200206104727g:plain
Unity でテスト!

Android でもやってみました。こんな感じでした。

f:id:shikaku_sh:20200206104819j:plain:w400
うまくできた!

サンプル

今回つくったプログラムは GitHub に公開しておきます。

AAR を使用したサンプル

.so ファイルを直接インポートしたサンプル

書ききれなかった部分があったので、補足として、もうひとつ記事「Unity SQLite の使い方 補足」を掲載します。こちらも参考になれば幸いです。

参考

Using SQLite: Small. Fast. Reliable. Choose Any Three.

Using SQLite: Small. Fast. Reliable. Choose Any Three.

SQLite入門 第2版

SQLite入門 第2版


  1. 一般的な Linux ディストリビューションとは異なるので、このあたりは wiki なども参考に。

  2. Unity マニュアルより「AAR は、推奨される Unity の Android アプリケーションのプラグイン形式です。」(URL)

  3. Android の ARM についても、認識が不足しているときは「Wiki - ARM architecture」や「Qiita - [ANDROID]CPUとアーキテクチャとABIの関係」などを参考に。

  4. 詳細は Untiy ブログの記事「Android サポートに関するお知らせ:64 ビット対応および App Bundle サポートの 2017.4 LTS バックポート」などを参考に。