2020/02/12

Unityリソース運用システム設計

WRITER: m.mine

こんにちは。開発G所属のエンジニアです。

ソーシャルゲームの開発と運用上リソースのビルドと更新は必ず必須になっています。

ソーシャルゲームの場合、Unityのリソースは主にAssetBundleとしてビルド、ダウンロード、更新を行います。

 


1.リソースバージョン設計


リソースバージョンはリソースタイプによって、2種類に分けました。

  1. Master Data
  2. AssetBundle + Rawファイル実運用上二つのバージョン管理になります。

マスターデータのバージョンを別管理にしたのは、マスターデータは頻繫に更新されますので、開発効率と運用効率を改善するため、マスターデータはUnityに依存しないように設計しました。

Master Dataの改善と運用

管理画面上設定例:

Application version ,  Asset Version ,MasterData Version

 


2.Assetの種類設計


  • AssetBundle
  • Raw
  • MasterData

AssetBundle

Unityのリソース、texture、prefabなど

Raw

Unityに依存しないリソース、movieなど
Androidの場合MovieはDecompressまたはメモリからロードした場合、再生できない問題があります。
AssetBundleにし、1. AssetBundleをロード、2. Fileとして吐き出し、3. Movieを再生する方法もありますが、直接Rawファイルとして、ローカルにDownloadした方が、AssetBundle.Load分負荷がかからないので、Unityに依存しないようにしました。

MasterData

ExcelからCsvとして吐き出し、csvを一つのjsonにまとめ、jsonを圧縮、ロード時にメモリに一括展開

 


3.アセットロケーション設計


運用上アセットは4種類必要になります。

  1. Build In Resources
  2. Build In Streaming Asset
  3. 初期Download
  4. 都度Download

Build In Resources


できる限り使わないこと、使う場合はResource Unloadで綺麗に解放すること、たとえFontなどが参照に残った場合、メモリが無駄に使われます。

 

Build In Streaming Asset


Build Inに入れるリソースは積極こちらを使いましょう。
注意点:Androidの場合はwebrequestを利用してアクセスすること

初期Download


多くのソーシャルゲームと同じく、チュートリアル完了したら、初回一括Download

都度Download


運用上イベント、ガチャなどのリリースより増えていくリソース

 

Rawファイルの格納場所


Rawファイルは基本UnityのImportが不要です。Rawファイルの無駄なImportが走らないようにするため、今回はRaw対象ファイルはUnityの
Assets/StreamingAssets/rawフォルダーの中に入れことにしました。Jenkinsビルド時Assets/StreamingAssets/rawを暗号化し、一定のネーミングルールでアセットバンドル名ルールと合わせます。
Applicationのビルド時にはAssets/StreamingAssets/rawフォルダーを自動削除して、ビルド行います。

 


4.設定Editor拡張


  • ロケーション設定
  • バンドル名設定
  • LinkXml生成(アセットバンドルのみ参照しているScript対応)
  • ビルドOS
  • バンドルビルド

全体画面


ロケーション設定


Editor上はFolderまたはファイルの右クリックで設定、またはScriptで一括設定できるようにします。

  • Initial(初期ダウンロード対象リソース)
  • Partial(都度ダウンロード対象リソース)
  • S(StreamingAsset対象リソース)

 


5.リソースDownload種類


  • MasterData
  • Initial
  • Partial

     

     
  • 用意したResourceManager

     

 


6.リソースLoad And Unload


  • Streaming Asset Load
  • Remote Asset Load
  • Resource Unload
  • 同期、非同期Load
  • await実装

 

Streaming Asset Load


初期ゲーム起動から初期ダウンロードまで、Streaming Assetを参照(Streaming Assetに含まれなかったAssetは自動でRemoteからDownloadし参照するように)

 

Remote Asset Load


初期Downloadが完了後、Remoteのみ参照するように(Streaming AssetとしてロードされたAsset Bundleを破棄し、Remoteのみ参照する)

 

Resource Unload


常駐Asset(例:UiのAtlasなど)として設定したリソースはStreaming Mode -> Remote Modeに切り替わるタイミングでUnload

それ以外のAssetは適切なタイミングでUnloadを行うように実装

 

同期、非同期Load


Defaultは非同期ロード、明示的useAsyncをfalseとして呼ばれた場合のみ同期Loadを行う。

同期の場合:

Assetを暗号化する場合、LoadFromMemory またはLoadFromStream

Assetを暗号化しない場合、LoadFromFile

非同期の場合:

Assetを暗号化する場合、LoadFromMemoryAsync またはLoadFromStreamAsync

Assetを暗号化しない場合、LoadFromFileAsync

非同期の場合同じフレームで同じAssetBundleのLoadが走る可能性がありますので、Lockをかける必要があります

semaphoreslimでロックをかけることは可能だが、パフォーマンスが悪かったので、下記ように一フレームを待つようにしました。

 

await実装


今回はリソースLoadはすべてawaitとして実装しCallBackをなくしました。

 


7.リソースDownload


  • http+複数スレッドDownload
  • http2Download
  • Downloadエラー処理
  • Downloadキャンセルとリトライ処理

 

http+複数スレッドDownload


http + 複数スレッド

 

http2 Download


Akamai 、Cloudfrontなどhttp2対応済みなので、http2のDownloadを検証しました。

MaxConcurrentStreams 128
InitialStreamWindowSize 65535
MaxFrameSize 16384

http  1スレッドの場合より 3 、4倍速い

http 8スレッドの場合より、遅い

多機種での検証はまだですが、機種またはCPU数によって、http、http2切り替えた方がよさそうでした。

CloudFrontの場合http2を有効にした場合、httpsでアクセスするとhttp2.0、httpでアクセスするとhttp1.1になっているようなので、たとえCPU数が4以下の場合はhttp2を使用し、CPU数が8以上の場合http(8スレッド)のほうが良いかも

しりません。

Downloadエラー処理


通信不安定などが原因でエラーが発生した場合のハンドリング

複数スレッドを考えなければならないので、エラー処理が発生した場合、次の処理をQueueに入れでスレッドに処理が入らないようにして、現在の実行中のスレッド処理を待つ必要があります。

全部のスレッド処理が完了したら、エラーハンドリングにエラー処理を任すように実装しました。

 

 

Downloadキャンセル処理とリトライ処理


Download中エラーが発生した場合、キャンセルまたはリトライ処理が必要になります。

今回はCallBackではなく、すべてをawait化にしましたので、特別な処理を行う必要があります。

もしAddressable Asset Systemのawaitを使用する場合も下記ようなキャンセル、リトライ処理実装が必要かもしりません。

 

 


8.Downloadフォルダー管理


  • Unity Application.temporaryCachePath問題
  • Android ファイルシステム問題

 

Unity Application.temporaryCachePath問題


iOSの場合システム側で勝手にApplication.temporaryCachePath(Library/Caches)のファイルを削除する場合があります。
そのため、Unity Cacheみたいに別フォルダーにDownloadして管理するほうが良いと思います。
たとえば:Library/Downloadフォルダーを作成し、リソースのDownload先はLibrary/Downloadにします。バックアップ対象外にするのを忘れずに

※Application.persistentDataPathにすることも可能ですが、あまりにもサイズが大きい場合はrejectになります。

 

Android ファイルシステム問題


Androidのファイルシステムは製造メーカー、AndroidOSバージョンによって、ファイルシステムの動作がかなり違いがあります。
一つのフォルダーに多いファイルが格納されている場合、端末によってGetFiles、CreateFileのパフォーマンスは5倍以上も差が出る場合があります。
それで、ファイルの名前をHash化しフォルダーを分けて保存したほうが良いと思います。

のような感じでフォルダーをを分けた方が良いと思います。

 


9.Sprite Atlasのアセットバンドル化


描画バッチ化のためUnityでよく使う2D Atlasは、Sprite Packerと Sprite Atlasがあります。

今回はSprite Atlasを使用することにしましたので、Asset Bundle化するための注意点を書きたいと思います。

Window -> 2D -> Sprite Packer
Sprite Packer Mode: Enabled For Builds または Always Enabledに設定します。

設定:
1.Include In Buildのチェックを外す

2.Allow Rotationのチェックを外す

3.Tight Packingのチェックを外す

 

ロード時注意点

AssetBundle化にしますので、

で実装します。
SpriteAtlasManager.atlasRequestedはSprite Atlas化された画像がロードされるとき必要なAtlasを求める処理です。

注意点:

  1. 最初Requestを返した場合、_uiSpriteAtlasの参照が残っている限り2度requestは来ません。
    再ロードをするためには、まずAssetBundleをUnloadし、且つResources.UnloadAsset(_uiSpriteAtlas);をする必要があります。
    例えばリソースをStreamingModeからRemoteModeに変更する場合など必要です。
  2. SpriteAtlasManager.atlasRequested += OnAtlasRequestedの タイミング
    この関数が登録される前に、どちらかのPrefabにAtlas画像の参照が入ってしまい、Requestを返すことができなかった場合2度Requestが来ないので、ゴミ画像が表示されてしまいます。

 


まとめ


Unity2019.3のリリースよりAddressable Asset Systemが正式にリリースされ、使いやすくなったと思いますが、
AssetBundle以外のファイルバージョン管理、一括Download機能、ダウンロードプロトコル(http2)切り替え機能が必要だったので、今回は自作することにしました。

m.mine

m.mine

開発グループ
シニアマネージャー