Unity は Sqlite3 と lua-protubuf を通じてゲーム アーカイブを作成します

明けましておめでとうございます、明けましておめでとうございます〜

インディペンデント ゲーム開発者 (実際には作るものは何もありません) がスタンドアロン ゲームを作りたい場合は、アーカイブが必要です。
アーカイブには、xml、json、PlayerPrefs、sqlite など、さまざまな方法があります。
上司と十分にコミュニケーションをとった後、一般的に他の方法よりも優れている sqlite を使用してこれを実行することにしました。(理由は忘れましたが)。

それで……
ここに画像の説明を挿入

まず私のバージョン構成について話しましょう:
Unity2019.4.10f1
Xlua
Sqlite3
lua-protobuf

次に、アーカイブのプロセスについて説明します。
Sqlite3を使用してデータベースを作成します。
ここに画像の説明を挿入
それらは、アーカイブ ID、作成タイムスタンプ、最終アーカイブ タイムスタンプ、およびアーカイブ データです。アーカイブされたデータは、Blob タイプを使用して保存されます。
ここに画像の説明を挿入

アーカイブされたデータはペタバイト単位のバイナリ ストリームに変換されて保存されます。

エピソード 1:
データベースには、role_data、money_data、equip_data など、複数の種類の BLOB データが存在する場合があります。
プロト データでは、RoleRoot、MoneyRoot、EquipRoot などの複数のメッセージ タイプを使用することもできます。
設計方法、データベースの属性、プロトのメッセージ タイプの定義方法はプロジェクトによって異なります。
上司はこのアーカイブ制作方法について長い間私とコミュニケーションをとっており、0.5日が無駄になりました。

それでは本文を始めましょう。

PB部

Google が作成した protobuf は lua をサポートしていないため、インターネットを検索して、すでにビルドされておりサードパーティ ライブラリを備えた xlua の git プロジェクトを見つけました。Plugins ディレクトリを自分のプロジェクトに取得します。
具体的な操作はこの記事で見ることができるので、詳細は説明しません。
xLua での lua-protobuf の使用

C# でファイルのバイナリ ストリームを読み取り、それを lua に渡し、pb.load を通じてロードします。

//PB文件路径
public static byte[] PB_SCHEMA
{
    
    
    get
    {
    
    
#if UNITY_EDITOR
        string filePath = Application.dataPath + "/../ExternalData/Proto/saveproto.pb";
#elif UNITY_STANDALONE
        string filePath = Application.dataPath + "/Resources/Proto/saveproto.pb";
#elif UNITY_ANDROID
        string filePath = Application.persistentDataPath + "/Proto/saveproto.pb";
#else
        string filePath = Application.dataPath + "/../ExternalData/Proto/saveproto.pb";
#endif

        byte[] pbBytes;
        if (File.Exists(filePath))
        {
    
    
            FileInfo fileInfo = new FileInfo(filePath);
            pbBytes = new byte[fileInfo.Length];
            FileStream file = fileInfo.OpenRead();
            file.Read(pbBytes, 0, Convert.ToInt32(file.Length));
            file.Close();
        }
        else
        {
    
    
            TextAsset file = ResourceMgr.Ins.LoadTextAsset("Proto/saveproto.pb", true);
            pbBytes = file.bytes;
        }

        return pbBytes;
    }
}

ここのパスは私のプロジェクトのパスとして読み取られます。IOSもやるつもりはないので、急にIOS版が出た場合にデバッグにどれくらい時間がかかるかわかりませんが…。

次に Lua の側面ですが、非常にシンプルかつ大まかです。

--加载pb.dll来转二进制和解二进制
local pb = require "pb"

if pb.load(GlobalDefine.PB_SCHEMA) then
    Debug("pb文件加载成功")
end

エピソード 2:
最初は、記事の例と同じように、loadfile を使用して pb ファイルを読み込みました。
ただし、このパスの相対パスで何が起こっているのかわかりません。インターネット上で見つからないので(はしごがひざまずいている、Baiduで検索しました)、
絶対パスを取得するためにC#で静的変数を作成しましたが、使用できます。
しかし、PC パッケージを入力した後、pb ファイルがリソース ディレクトリに配置されていることを思い出しました。これは非常に読みにくいものです。
そこで、ファイルを直接読み込んでloadメソッドで読み込むバイナリストリームに変更しました。不快で、0.8日無駄になりました。

エピソード 3:
コンパイル経験のないベテランは、自分でコンパイルしようと考えないことをお勧めします。lua-protobuf 命令を使用してpb.dll をコンパイルしようと
しました。コンパイルはコンパイルされましたが、それを放り込んだところ、何も正しくありませんでした...そこで、xlua が既にサードパーティのライブラリを継承していることを確認したとき、プラグインを取得して使用しただけで、自分でコンパイルする気はありませんでした。全然。前にコンパイルしに行ったので2.2日無駄にしました。

これでpb部分は完了です。
実際、アーカイブにすべての値を格納するテーブルが 1 つだけある場合は、pb.encode からのバイナリ ストリームを直接ファイルとして保存でき、アーカイブとしても使用できると思います。

ただし、それはプロジェクトごとに異なります。私はむしろ、より完全なセットを作成したいと考えています。こうして悪夢が始まる…。

Sqlite3 パート

まず、エディタと PC が正常に動作することを確認する必要があります。
まずsqlite3の公式サイトにアクセスし、PCに必要なdllをダウンロードします。
どのSQLite ダウンロード ページをダウンロードする必要があるかは、ご自身で判断してください。
ここに画像の説明を挿入
さらに、Mono.Data.Sqlite.dll が必要です。このファイルは、Unity インストール ディレクトリ
2018: C:\Program Files\Unity\Unity2018\Editor\Data\Mono\lib\mono\2.0\Mono.Data にあります。 Sqlite.dll
2019: C:\Program Files\Unity\Unity2019\Editor\Data\MonoBleedingEdge\lib\mono\2.0-api\Mono.Data.Sqlite.dll
このライブラリを Plugins フォルダーに置くと、PC が起動します。

記事によっては System.Data.dll も入れるべきと書かれていることがありますが、これは実際の状況によりますが、追加するとさまざまなトラブルが発生します。

エピソード 4:
「System.InvalidOperationException: DataReader がアクティブな間は CommandText を設定できません」がそのときに発生しました。BaiduとBingの両方を検索しましたが、理由はまったく見つかりませんでした。その後、目立たない記事で、2019 年にはすでに古い互換性の問題があり、2018 年の方が安定しているため、2018 年バージョンがコピーされたことを知りました。さて、試してみましたが、パッケージを取得できず、競合を伝えました。単純に削除したら思いの外解決しました…2.5日の無駄でした。

ここにデータベースを開くコードを貼り付けます

#if UNITY_EDITOR
    private readonly static string saveFilePath = Application.dataPath + "/../SaveData/";
#elif UNITY_STANDALONE
    private readonly static string saveFilePath = Application.dataPath + "/../SaveData/";
#elif UNITY_ANDROID
    private readonly static string saveFilePath = Application.persistentDataPath + "/SaveData/";
#endif

public void OpenConnect()
{
    
    
    try
    {
    
    
        //存档存放路径
        if (!Directory.Exists(saveFilePath))
        {
    
    
            Directory.CreateDirectory(saveFilePath);
        }

        //基本内容数据库
        string basicStr = "Data Source = " + saveFilePath + fileName;
        connection = new SqliteConnection(basicStr);
        connection.Open();
        Debug.Log("Connect to SaveDatabase Succ");
    }
    catch(Exception e)
    {
    
    
        Debug.LogError(e.ToString());
    }
}

いくつかの記事で、SqliteConnectionで渡される文字列は「URI = file:」で始まる必要があると記載されていましたが、ソースコードを確認したところ、「Data Source =」を使用することが可能です。

ここでBlobの問題について話しましょう。
より単純なステートメントの場合は、文字列を直接渡します。

public void SetGameSaveValue(int id, string key, int value)
{
    
    
    string sql = string.Format("update basic set {1} = {2} where id == {0}", id, key, value);
    ExecuteQuery(sql);
}

ただし、blob は異なり、char[] の方が適していますが、pb はバイナリ ストリームに変換されているため、byte[] を使用しても問題ありません。ただし、このデータは文字列を結合して処理することができないため、

public void SetGameSaveValue(int id, string key, byte[] value)
{
    
    
    SqliteCommand command = connection.CreateCommand();
    command.CommandText = string.Format("update basic set {1} = @value where id == {0}", id, key);
    command.Parameters.Add("value", DbType.Binary).Value = value;
    ExecuteQuery(command);
}

それでは、ExecureQuery メソッドについて説明します。

private DataRow ExecuteQuery(string sql)
{
    
    
    SqliteCommand command = connection.CreateCommand();
    command.CommandText = sql;
    return ExecuteQuery(command);
}

private DataRow ExecuteQuery(SqliteCommand command)
{
    
    
    SqliteDataReader reader = command.ExecuteReader();
    command.Dispose();

    //获取数据
    DataTable dTable = new DataTable();
    dTable.Load(reader);
    reader.Close();

    //只返回查询到的第一行数据
    if (dTable.Rows.Count > 0)
    {
    
    
        return dTable.Rows[0];
    }

    return null;
}

ただし、ここで問題があり、DataRow の値はオブジェクトなのでボックス化を解除する必要があるのですが、このとき注意しなければならないのがnull 値の問題です。

Sqlite3 Android 部分

Android を使用する場合は、libsqlite3.so ファイルが必要です。Baidu を 1 つずつ使用することも、他の人の投稿から取得したダウンロード リンクをここに示します。

補えない場合は、System.data.dll を Plugins に追加する必要があるかどうかを確認してください。エピソード 4 があるかもしれませんが、2018 では試していないのでわかりません。

コンパイルされたパッケージでは、予期せぬことが何も起こらなかった場合、LogCat で「advapi32 が見つかりません」というメッセージが表示されます。これは少し古く、System.Data.DataTable が見つからないためです。したがって、この段落は変更する必要があります

private object ExecuteQuery(SqliteCommand command, string resultKey = null)
{
    
    
    SqliteDataReader reader = command.ExecuteReader();
    command.Dispose();

    object result = null;

    //获取数据 只以第一条搜索到的数据为准
    if ((resultKey != null) && reader.HasRows)
    {
    
    
        reader.Read();
        for (int index = 0; index < reader.FieldCount; index++)
        {
    
    
            if (resultKey == reader.GetName(index))
            {
    
    
                result = reader[index];
                break;
            }
        }
    }

    reader.Close();
    return result;
}

この書き方はプロジェクトごとに異なります。固定の主キーを探しているため、必要なデータは 1 つだけです。

第5話:
Sqliteの公式サイトで、Androidに必要なaarファイルをダウンロードしました(このファイルのみ)。Baidu の慣例によれば、「sqlite3.so が見つかりません」「libsqlite3.so が見つかりません」と表示され、パッケージを解凍して中の名前が「libsqliteX.so」であることがわかったので、次のように変更しました。 libsqlite3。すると完全に崩壊してしまいました==。それは忘れて、次はインターネット上で使いましょう。1日が無駄になった。

これらがすべて完了すると、最終的に Sqlite+pb アーカイブが PC と Android の両方で実行できるようになります。ほぼ10日かかりました。非常に難しい。
こちらのご要望もいただければよりスムーズになると思います。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qql7267/article/details/113757676