Unityをいじってみた(その3〜データのダウンロードと保存方法〜)
続きです。実際にゲームを作ることでGameObjectやScriptに関する知識はたくさん溜まりましたが、すべてを書くのは果てしないので、このゲーム独特の内容を書いていきたいと思います。
データの配置と読み込みについて
このゲームは、UIイメージの他に、音源や譜面にBMSデータ(※)を使用しています。主に使うデータとしては、「イメージ画像」「音源(WAV)」「譜面(テキスト)」の3種類になります。※フリーのBeatmaniaクローンゲームのデータ。音源等には著作権があるため、あくまで個人利用。
「イメージ画像」「音源(WAV)」といった素材はUnityのProjectに配置することで、Unityに最適化された状態に変換されるようです。またこの時、ProjectのResourcesフォルダに配置することで、Script側からオブジェクトとして簡単に呼び出すことが可能になります。
一方で「譜面(テキスト)」の場合はUnityで利用する素材ではないため、テキストファイルとして読み込む必要があります。読み込みには.NetライブラリのFileInfoクラスなどを使います。また、Unityはビルド時にアセットを統合してしまうため、実機で実行するとファイルをロードすることができなくなります。統合されたくないデータはProjectのStreamingAssetsというフォルダに配置することで、そのままの状態でファイルをロードできます。
データの保存領域について
スマホアプリとしてゲームを配布する場合、データ容量に注意する必要があります。Android、iPhoneともに、アプリのサイズは最大50MBに制限されています。BMSファイルは1曲で数MB〜数十MBになるため、とてもアプリに含めて配布することができません。そこで、BMSファイルをダウンロードしてアプリ領域以外にデータを保存する領域が必要になります。
1.キャッシュ領域
キャッシュ領域はデータ容量の上限はありませんが、その代わり永続性はありませんので、ゲームデータを保存するのには向きません。
Unity - スクリプティング API: Application.temporaryCachePath
2.永続領域
キャッシュではなく永続的にデータを保存できる領域です。ゲームデータを保存するのにも良さそうです。しかし、iPhoneアプリでここをたくさん使う場合、リジェクト対象となるそうです。
Unity - スクリプティング API: Application.persistentDataPath
3.Asset Bundle
Asset Bundleはアプリ領域にデータを追加保存するようです。ソーシャルゲームなどで一般的に使われているのはこれだと思います。BMSファイルを配布するにはこれが一番良いとおもいきや、Pro版のみの機能ということであえなく断念しました。
参考:【Unity】AssetBundleを使わずに動的にデータを取得するゲームを作る | バイナリ覚書
Asset Bundleもどきを作る
Asset BundleはPro版でしか使えないので、永続領域にBMSファイルをダウンロードして保存し、そこからデータを読み込むことにしました。この場合、iPhoneではリジェクト対象となるのでAndroidだけをターゲットにすることにしました。実装までには次の3つの機能を作成します。
1.ダウンロード
データのダウンロードは、.NetのWWWクラスを使うことで実現できます。ファイルがローカルに存在するか確認し、なければダウンロードします。
string filename = "bms.zip"; string path = "file:///" + Application.persistentDataPath + "/"; string url = "http://localhost/bmsdata/"; if (File.Exists (Application.persistentDataPath + "/" + filename)) { Debug.Log ("Find local file." + Application.persistentDataPath + "/" + filename); } else { Debug.Log ("Can't find local file, Downloading.."); WWW www = new WWW (url + filename); while (!www.isDone) { } File.WriteAllBytes (Application.persistentDataPath + "/" + filename, www.bytes); }
2.Zip解凍
BMSデータはzip圧縮しているので、ゲームをプレイする前に解凍します。最初は.NetのライブラリのZipFileクラスを使おうとしたのですが、Unityが利用するMonoは.Netの3.5までしか対応しておらず、ZipFileクラスは4.5で追加されたライブラリのため使うことができませんでした。そこで、CodePlexにあったDotNetZipライブラリを使用しました。
外部ライブラリを追加する場合はUnityのProjectに追加するだけでScriptからusingできるようになります。
以下はzipファイルから特定の拡張子のファイルだけを解凍するサンプルコードです。ExtractExistingFileAction.OverwriteSilentlyを指定することで、既にファイルが存在する場合も上書きします。
using Ionic.Zip; using (ZipFile zip = ZipFile.Read (Application.persistentDataPath + "/" + filename)) { zip.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently; foreach (ZipEntry entry in zip) { if (Regex.IsMatch (entry.FileName, @"\.(wav|WAV|bme|BME)$")) { Debug.Log ("decompress: " + entry.FileName); entry.Extract (Application.persistentDataPath, ExtractExistingFileAction.OverwriteSilently); } } }
しかしこのライブラリは、Windowsにしか対応していないため、与えられたパスの'\'を'/'に置換するという処理が入っていました。そのため、Macなどの環境ではパス指定ができず読み込みエラーとなりました。(.NetのライブラリなのでWIndowsしか想定していないのは当たり前といえば当たり前ですね)。これをモンキーパッチ的にLinux用に修正されたモジュールがあり、それを使用することでこの問題はひとまず回避出来ました。
DotNetZip Library - View Issue #15236: "Path is empty" when extracting in a linux system.
3.AudioClipの作成
Unityでは音源をAudioClipのインスタンスにして再生します。ダウンロードした音源はWAVファイルなので、これをAudioClipに変換する必要があるため、以下を参考にして実装しました。正直、このレベルになってくると自分の力では相当難しいので、Githubにソースが公開されていて助かりました。
Post Position 【Unity】 WAVファイルからAudioClipを自前で生成するサンプルを作ってみた
以上で、Asset Bundleもどきができました。
できた後で知りましたが、Androidアプリの場合、ダウンロードなどはせずにSDカードに直接データを入れる方法を取るほうが一般的みたいですね。
次に続きます。