この記事は大阪工業大学 Advent Calendar 2020の18日目の記事です。
#はじめに
はじめましての方ははじめまして、それ以外の人はこんにちは。
今年度大学に行かなさ過ぎてただのニートと化してるnihuといいます。
軽く自己紹介でもした方がいいかなと思ったんですけど、僕のTwitterのホーム画見たら大体わk
需要がない気がしたので割愛します。
あと今回のはやってること自体は難しくないですがunityちょっと知らないとわからないかもしれない...申し訳ない...
#1.目的
UnityでSQLを扱うのに便利なSQLiteUnityKitというものがあります。
詳しくは他にわかりやすい記事かいっぱいあると思うのでここでは詳しく書きませんが、
これを導入すると簡単にデータベースを扱えるようになるアプリです。
で、このアプリ、データベースファイルを読み込む先がstreamingAssetsからに限定されています。
streamingAssets以外に置くとビルド後に場所が変わってしまったりするから当然と言われれば当然なのですが、
streamingAssetsに置くと作成したアプリがデスクトップ向けのものだとフォルダの中にデータベースファイルがそのまま入っていて
少し知っている人なら簡単に中身を見れるという問題があります。(まだ初心者で知らないだけの場合もあるので隠蔽とかする方法あるなら誰か教えてください泣いて喜びます)
前使ったときは見られても問題ないなーぐらいに思ってたんでスルーしてたんですが、
今後作りたいと考えてるものの中で見られると嫌だなというケースが出てきたので、なんか方法ないかなと思って考えたのがResourcesフォルダから読み込む方法です。
(unity詳しい人ならAssetBundleじゃないのって思うと思うんですけどまだ使ったことないのでResourcesからで大目に見てください...)
とりあえず考えたはいいけど実際できるのかと調べた結果できたのでこれからやった方法を纏めていきます。
#2.方法の模索
まずSQLiteUnityKitのSqliteDatabaseのコンストラクタを見てデータベースのファイルをどうやって読み込んでいるか見てみました。
public SqliteDatabase (string dbName){
pathDB = System.IO.Path.Combine (Application.persistentDataPath, dbName);
//original path
string sourcePath = System.IO.Path.Combine (Application.streamingAssetsPath, dbName);
//if DB does not exist in persistent data folder (folder "Documents" on iOS) or source DB is newer then copy it
if (!System.IO.File.Exists (pathDB) || (System.IO.File.GetLastWriteTimeUtc(sourcePath) > System.IO.File.GetLastWriteTimeUtc(pathDB))) {
if (sourcePath.Contains ("://")) {
// Android
WWW www = new WWW (sourcePath);
// Wait for download to complete - not pretty at all but easy hack for now
// and it would not take long since the data is on the local device.
while (!www.isDone) {;}
if (String.IsNullOrEmpty(www.error)) {
System.IO.File.WriteAllBytes(pathDB, www.bytes);
} else {
CanExQuery = false;
}
} else {
// Mac, Windows, Iphone
//validate the existens of the DB in the original folder (folder "streamingAssets")
if (System.IO.File.Exists (sourcePath)) {
//copy file - alle systems except Android
System.IO.File.Copy (sourcePath, pathDB, true);
} else {
CanExQuery = false;
Debug.Log ("ERROR: the file DB named " + dbName + " doesn't exist in the StreamingAssets Folder, please copy it there.");
}
}
}
}
色々書かれてますが要約するとandroid以外はデータベースをPersistentDataPathにコピーしてコピーしたものを参照している仕組みだとわかります。
つまりResourcesフォルダからデータを読み込んで同じところにファイルを生成できればあとは全く同じでも問題ないということになります。
ただResourcesフォルダ内のものはよくわからん形式に圧縮されてるので何かいい方法はないかと調べてると以下のサイトをみつけました。
これを見るとTextAssetというクラスを使うとResourcesそのままのデータを読み込めるらしいのでつまり
ファイルを読み込む→パス作ってSystem.IO.File.WriteAllBytesで読み込んだバイナリデータをファイルに書き込む→ファイル作れた^q^
という流れを汲めば恐らく上手く動作するはずだと考え、次にSqliteDatabaseの書き換えに移りました。
#3.ソースコードの書き換え
ほんとは上の方法で正しくコピーできるかとかテストしたんですが、問題なく動いたのと書く時間がないので割愛して書き換え後のSqliteDatabaseのコンストラクタのコードを記述します。
public SqliteDatabase (string dbName){
pathDB = System.IO.Path.Combine (Application.persistentDataPath, dbName+".db");
//original path
//string sourcePath = System.IO.Path.Combine (Application.streamingAssetsPath, dbName);
//if DB does not exist in persistent data folder (folder "Documents" on iOS) or source DB is newer then copy it
if (!System.IO.File.Exists (pathDB) /*|| (System.IO.File.GetLastWriteTimeUtc(sourcePath) > System.IO.File.GetLastWriteTimeUtc(pathDB))*/) {
TextAsset textAsset=(TextAsset)Resources.Load(dbName);
System.IO.File.WriteAllBytes(pathDB, textAsset.bytes);
/*if (sourcePath.Contains ("://")) {
// Android
WWW www = new WWW (sourcePath);
// Wait for download to complete - not pretty at all but easy hack for now
// and it would not take long since the data is on the local device.
while (!www.isDone) {;}
if (String.IsNullOrEmpty(www.error)) {
System.IO.File.WriteAllBytes(pathDB, www.bytes);
} else {
CanExQuery = false;
}
} else {
// Mac, Windows, Iphone
//validate the existens of the DB in the original folder (folder "streamingAssets")
if (System.IO.File.Exists (sourcePath)) {
//copy file - alle systems except Android
System.IO.File.Copy (sourcePath, pathDB, true);
} else {
CanExQuery = false;
Debug.Log ("ERROR: the file DB named " + dbName + " doesn't exist in the StreamingAssets Folder, please copy it there.");
}
}*/
}
}
元々のソースコードでいらない部分はコメントアウトして残しています。色々適当だったり殆ど原形なかったりしますがまあこれで行けると思います(適当)。
そしてテスト用に書いたソースコードはこちらです。
void Start()
{
string test="1";
SqliteDatabase sqliteDatabase=new SqliteDatabase("testdata");
string testQuery=string.Format("select * from test where id = '{0}'",test);
DataTable dataTable=sqliteDatabase.ExecuteQuery(testQuery);
string name="";
foreach (DataRow dr in dataTable.Rows){
name=(string)dr["name"];
Debug.Log(name);
}
}
これで必要なものはそろったのでtest.csを適当なオブジェクト作ってアタッチしてみてテストプレイを実行してみます。
ヤッターーー!!!
というわけでうまく作動できました。
#おわりに
絶対他にいい方法ある気しかしない(震え)
初め調べたとき似たようなの出てこなかったので作るかってなったけど後で調べてたらしっかり見てはないけどバインドして隠蔽云々書かれている記事見つけて「これ...いるんか?」ってなったけど見なかったことにしました()
けど今の自分ができるなかでやってみて一応うまくできたので良かったかなと思います(難しいことはしてないけど)
上にも書きましたけどこんなことしなくてもファイル隠せるよなど詳しい人いたら教えてください...
(おわり)