Cocos2dx製アプリのデータをUnity製アプリに移行する方法

2021年2月12日

Cocos2dx(js) で作成したアプリを Unity で作り直してアップデートした際、アプリ領域に保存したデータの移行を行ったので手順について記載します。

Cocos2d-xのロゴUnityのロゴ

移行させるデータについて

Android における SharedPreferences と同様に、Cocos2dx では cc.sys.localStorage によってテキストや数値といったデータを保存できます。

// 例:キーと値のペアで数値データを保存する
cc.sys.localStorage.setItem("number", 123);
cc.log(cc.sys.localStorage.getItem("number"));

Cocos2dx でこのように保存したデータは SQLite3 によるデータベースとしてアプリ領域に保存されます。保存されるデータベースのファイル名は jsb.sqlite です。OSごとの保存パスは以下のようになっています。

Android では通常
data/data/[パッケージ名]/databases/jsb.sqlite

iOS では
/var/mobile/Containers/Data/Application/[アプリ固有ID]/Documents/jsb.sqlite

従って、Cocos2dx の保存データを Unity で読み込むためには、SQLite のテーブルから情報を取り出す処理が必要になります。

UnityでSQLiteを使うには

SQLite を扱う基本的なコードは以下からダウンロードできます。

GitHub - Busta117/SQLiteUnityKit: Framework to connect with a SQLite database from Unity for iOS, Android, MAC , Windows

このフレームワークに含まれる2つのソースコード(DataTable.cs、SqliteDatabase.cs)とSQLiteの実行ファイル libsqlite3.so を使用します。libsqlite3.so が含まれていますが、最新版をダウンロードしたい場合は"SQLite Download Page"から取得できます。

もしファイルが適切なディレクトリにあるにも関わらず、以下のようなエラー(DllNotFoundException)が出た場合はダウンロードし直してみると良いかも知れません。

libsqlite3.soでDllNotFoundException

ダウンロードした libsqlite3.so を、使いたい Unity のプロジェクトの Plugins ディレクトリに保存します。OSごとに保存するディレクトリが以下のように異なります。

Androidの場合

実行環境別にコピーして保存します。

Assets/Plugins/Android/libs/x86/libsqlite3.so
Assets/Plugins/Android/libs/armeabi-v7a/libsqlite3.so
Assets/Plugins/Android/libs/arm64-v8a/libsqlite3.so

Androidで使うlibsqlite3.soの保存ディレクトリ

Unity のインスペクターで、各実行環境に向けて設定(Platform Setting > CPU)を変更しておきます。

ARM64向けのlibsqlite3.so

iOSの場合

以下のディレクトリに保存します。

Assets/Plugins/iOS/libsqlite3.so

SQLiteのデータベースを読み込むコード

libsqlite3.so の準備が終わったら、実際にデータを読み込むためのコードを用意します。

SqliteDatabase.cs を開き、コンストラクタから不要な機能を除去します。

	/// <summary>
	/// Initializes a new instance of the <see cref="SqliteDatabase"/> class.
	/// </summary>
	/// <param name='dbName'> 
	/// Data Base name. (the file needs exist in the streamingAssets folder)
	/// </param>
	public SqliteDatabase (string pathDB)
	{
		// 不要な機能を覗いたコンストラクタ
		this.pathDB = pathDB;
	}

また、マルチバイト文字を扱う予定があるのであれば、メソッド Prepare の書き換えを行います。

    private IntPtr Prepare(string query)
    {
        IntPtr stmHandle;

        // クエリのバイト数を取得するように変更
        int byteCount = System.Text.Encoding.UTF8.GetByteCount(query);

        if (sqlite3_prepare_v2(_connection, query, byteCount, out stmHandle, IntPtr.Zero) != SQLITE_OK)
        {
            IntPtr errorMsg = sqlite3_errmsg(_connection);
            throw new SqliteException(Marshal.PtrToStringAnsi(errorMsg));
        }

        return stmHandle;
    }

以上の修正を終えたら、これを用いて SQLite からデータを読み取る処理を行うスクリプトを作成します。

例えば、以下のような関数を作成し、アプリ起動時に実行するようにします。

void ConvertOldData() {

    // 初回起動に一度だけ行うようにする
    if (PlayerPrefs.GetString("Convert") == "DONE") {
        print("読み込み済み");
        return;
    }

    string dbFilename = "jsb.sqlite";

    // 見つかったSQLiteデータベースのパスを格納する変数
    string cocosDataPath = "";

    #if UNITY_ANDROID
        string packageName = "your.app.package.name";

        // 念の為、データベース保存の候補となりそうなディレクトリを検索
        string[] possibleDataPaths = {
            "data/data/" + packageName + "/databases/" + dbFilename,
            "data/user/0/" + packageName + "/databases/" + dbFilename,
            "/mnt/sdcard/Android/data/" + packageName + "/databases/" + dbFilename,
            "/storage/emulated/0/Android/data/" + packageName + "/databases/" + dbFilename,
        };
        foreach (string path in possibleDataPaths) {
            
            if (File.Exists(path)) {
                print("FILE FOUND AT: " + path);
                cocosDataPath = path;
                break;
            }
            print("SEARCHED: " + path);
        }
        
        if (cocosDataPath == "") {
            print("NO FILE FOUND");
            return;  
        }
        
    #elif UNITY_IOS
        cocosDataPath = Application.persistentDataPath + "/" + dbFilename;

        if (!File.Exists(cocosDataPath)) {
            print("NO FILE FOUND AT: " + cocosDataPath);
            return;
        }
    #endif

    // Cocos2dx で作成されるデータテーブル名は data なので
    string tableName = "data";
    SqliteDatabase sqlDB = new SqliteDatabase(cocosDataPath);

    // データ取得
    string selectQuery = "select * from " + tableName;
    SqliteDataTable dataTable = sqlDB.ExecuteQuery(selectQuery);

    // データベースにある全てのキーと値のペアを取得
    foreach (DataRow row in dataTable.Rows)
    {
        string key = (string)row["key"];
        string val = (string)row["value"];
        print(key + ": " + val);
    }
}

iOS では、保存パスは Application.persistentDataPath を使って簡単に取得できます。

Application.persistentDataPath + "/jsb.sqlite";

Android では、機種によって保存領域が異なる可能性があるため、念の為複数のディレクトリを確認していますが、通常は data/data/[パッケージ名]/databases/ 以下に保存されるはずです。

以上のコードによって、保存されたキーと値のペアが取得できますので、それをそのまま利用したり PlayerPrefs に保存し直したりすることができます。

Cocos2dxのデータを読み込む際の注意点

cc.sys.localStorage で保存したデータは Unity で読み込んだ場合、全て文字列として扱われます

よって、PlayerPrefs に保存し直したりする場合、元々数値やブール値で保存したものはパースによって数値(intやfloat)やブール値(true/false)に変換する必要があります。

// 数値の取得とパース
int result;
if (int.TryParse(val, out result)) {
    PlayerPrefs.SetInt(key, result);
}
float resultF;
if (float.TryParse(val, out resultF)) {
    PlayerPrefs.SetFloat(key, resultF);
}

// bool の取得とパース
bool getBool;
if (key == "flag") {
    getBool = (val == "true");
}

以上、Cocos2dx製アプリの保存データをUnity製アプリに移行する方法についてでした。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です