読者です 読者をやめる 読者になる 読者になる

ぷらこあ

雑記/ゲームアイデア帳/成果物ぺろり

【Unity道場10】アセット運用ベストプラクティス に参加しました

Unityメモ セミナー/勉強会

eventdots.jp

参加しました!私的メモです(ˊ꒳ˋ*)

公開された資料

speakerdeck.com

まとめ

  • Resourcesには動的に差し替えるコンテンツのみを入れる
    • Resources以下のファイル全てに対し、初期処理が発生する
    • Resources以下に多量のアセットを配置することで膨大な初期化処理が発生
    • Asset以下にある全てのResourcesが対象なので、外部アセットにも注意
  • UnityはInstanceIDを元にアセットの参照を行っている
    • InstanceIDが参照出来ないケースではMissingになるので注意
    • 特にAssetBundleアンロード時等で注意
  • AssetBundleの取得はUnityWebRequest + DownloadHandlerがオススメ
    • UnityWebRequestはUnity5.4から正式版になった
  • AssetBundleの圧縮形式はUnity5.3から採用されたLZ4がオススメ
    • ただし使用環境によって最適解は異なるので特徴見極める必要がある
  • ベストプラクティスを信じない
    • 環境やゲームの内容によって最適解は異なる

メモ

unityでアセットを利用する

アセットをインポートしてそのまま使用する

Resourcesフォルダを使用する

  • 動的に差し替えられるアセット
  • ファイル名(拡張子は無し)で取得
  • リリース後に変更出来ない
  • スクリプトにてコンポーネントに設定する
// Resources直下に置いたファイルを取得する
var spriteRenderer = GetComponent<SpriteRenderer>();
spriteRenderer.sprite = Resources.Load<Sprite>("FilePath");

AssetBundleを使用する

  • リリース済みのアプリにアセットを追加できる
  • asset群を束ねた(bundke)もの
  • Unityが使用可能なアセットを1つ以上格納するアーカイブ

アセットの依存関係

  • 依存するアセットも同時にロードされる
  • e.g) Prefab => Sprote

AssetBundleにおいて生成されるもの

  • Manifest
    • 依存関係を表示する
  • Asset AssetBundle
    • アセットを格納する
  • StremedSceneAssetBundle
    • シーンを格納できる
    • SeneManagerから呼び出せる
    • シーン情報とシーンが参照するアセット群が含まれる

AssetBundleManager

  • AssetStoreからダウンロード出来る
  • 機能
    • シミュレーター機能
      • AsserBundleをビルドせずにAssetBundleからロードした事にする
    • LocalServer機能
    • Build機能

アセットとオブジェクトのシリアライズについて

  • オブジェクトの状態を保存し、復元する
  • Unityはシリアライズに依存している
    • e.g) PrefabとSceneの関係
    • e.g) オブジェクトとアセットの関係
  • 実行時にでシリアライズしてオブジェクト構造を再現する
  • アセットインポート時(D&D時)
    • TextureやAudio, Model等のネイティブデータを参照するUnityEngine.Objectシリアライズ
    • ネイティブデータ: ハードウェアによってフォーマットが決定しているもの
      • プラットフォームによってTextureの参照する可能な形式が異なる
    • UnityEngine.Objectが中間に入ることで統一されたIFで各PF向けのネイティブデータが読み込める
  • UnityEngine.Object : データは1:1の関係
  • prefab/scene => GUID/LocalIDでUnityEngine.Objectにアクセス => .meta => ネイティブデータにアクセス

GUID

  • 重複しないIDでアセットを識別
  • GUIDハインポート時に背呈され .metaデータが失われない限り維持される
  • renameやパスの変更でも維持される
  • 以下にも割り当てられる

.meta

  • アセットをインポートする祭に作成される
  • metaを残したままファイルを上書きする事で参照/設定を維持したまま更新出来る
  • 初期設定は隠しファイルになっている

LocalID

  • GUID : .meta は1:1の関係
  • ただし1つのアセットに複数のアセットを持つ事がある
  • e.g) Atalas化したSprite
  • GUIDとは別にLocalIDにて識別する
  • .metaに記載されている&の後の数字がLocalID

アセットのライフサイクル

  • UnityEngine,Objectを経由したロードされる
  • 実行時にはGUID/LocalIDを変換したInstanceIDでアセットを参照する(高速化のため)
    • ゲーム起動時に全てのアセットに対して生成が行われる
  • 不要になったらアンロードするが、InstanceIDは残る

Resourcesについて

  • テキストベースでInstanceIDにアクセスし、任意のアセットを引き出す
  • Resourceフォルダ以下に配置したアセットにInstanceIDを登録する

内部動作的

  • アプリ起動時にInstanceIDとファイル名とフォルダパスをキャッシュする
    • Resources/Prefabに紐づいているアセットはキャッシュされない
  • アプリ起動時にLookup用のテーブルを作成する
  • 単一のファイルにシリアライズされる

デメリット

  • (Resourcesを不適切に使う事によって) アプリの起動時間やビルド時間が延びる
    • Resources以下に全てのアセットを配置することで膨大な初期化処理が発生
      • ルックアップテーブルも広がる
  • カスタムコンテンツの配信に向いていない
  • 差分更新が出来ない
  • アセットを圧縮できない
  • Androidはアクセスに追加コストが発生する

AssetBundle

  • AssetBundleのロード時にAssetBundle内に含まれるアセットのInstanceIDが読み込まれる
  • AssetBundleを跨いだ依存関係を設定することができる
  • AssetBundleの持つネイティブデータへInstanceIDを経由してアクセス
  • AssetBundleをアンロードするとInstanceIDが失われる
    • 参照先をUnloadした場合、参照もともUnloadし、再度Loadする必要がある
  • 文字列アクセス用のLookupテーブルが生成される
  • AssetBundle1会に月10kb=40kbのメモリ消費と僅かながらの展開のオーバーヘッドがある

AssetBundleの依存関係

  • 他のAssetBundleに依存する場合、InstanceIDを元に参照を解決する
  • 見つからない場合、Missingに設定される

AssetBundle.Unload

  • AssetBundle.Unload(false):AssetBundleから提供したInstanceIDを削除する
  • AssetBundle.Unload(true): 全てのInstanceIDを持つアセットを強制的に解放し、InstanceIDも削除する

AssetBundleの分割数

  • LZMAの場合、サイズが大きいと非常に長いローディング時間が発生する
  • CacheOrDownloadのキャッシュを利用すると高速ロード出来るが数が多すぎると起動時に負荷
  • LoadAllAssetsは2/3のアセットを一括で伃邑な、逐次ロードよりも早いかも
  • AssetBundleの分割数が少ない
    • (= アセットのサイズが大きい)
    • メモリ使用量が増える (memory等で読むと)
    • 読み込み時間が延びる (lzmaで読むと)
    • AssetBundleのビルド時、再構築の機会が増える
  • AssetBundleの分割数が多い
    • ビルドに時間が掛かる
    • リソースマネジメントが非常に複雑
    • ダウンロードの時間が延びる

暗黙の参照/重複アセット

  • AssetBundleは直接assetbundle nameを指定しないアセットもAssetBundleに含める
    • 複数のAssetBundleに含まれる場合がある
      • 例えばシーンをAssetBundle化する場合、そのシーンに含まれるアセットについて
      • 既にInstanceIDが割り振られていた場合、新しいInstanceIDを割り振られる
    • 特にShaderやTextureが危険
    • 対策
      • resource AssetBunde を用意し、シーンはresourceに対する参照を持たせる

AssetBundleの圧縮について

  • サイズ: LZMA <<<< LZ4 < 非圧縮
  • ロード速度: LZMA > LZ4 >>>>> 非圧縮
  • LZ4はUniy5.3以降に使用可能
  • LZMAを解答するには一旦すべてをメモリに展開する必要がある

AssetBundleの読み込み

  • AssetBundle.LoadfFomFiles
    • ヘッダを読み、ローカルストレージにアクセスするため最速
  • AssetBundle.LoadFromMemory (WWW.assetBundle)
    • メモリからAssetBUndleを構築してロードする
    • LZ4の場合、メモリにそのままコピーする
    • LZMAの場合、全て解答し、LZ4へ再圧縮する(5.3以降)
    • 使用するのはAssetBundleを暗号化したい場合くらい
  • WWW.CacheOrDownLoad
    • アセットをダウンロードし、Unityのキャッシュシステムでキャッシュする
    • LZ4/無圧縮の場合、メモリにそのまま保存する
    • LZMAの場合、全て解答し、LZ4へ再圧縮する(5.3以降)
    • 起動時にキャッシュの有無や削除判定を行う
    • 2回目以降はキャッシュから取得される
  • UnityWebRequest & DownloadHandler
    • DownloadHandlerでのデータ処理を指定する事でヒープを抑えられる

AssetBundleのアセットの読み込み

  • LoadAsset
  • LoadAllAssets
  • LoadAssetWithSubAssets
  • Asyncを設定するとアセットの読み込みをワーカースレッドで行う
  • 1フレームの処理時間は設定可能 (ThredPriority)
  • Unity5.3からUnityEngine.Objectのロードもワーカースレッドで実行する
    • 並列で一括で処理し、ロード完了後にAwakeが呼ばれる

内部キャッシュシステム

docs.unity3d.com

  • 指定のAssetBundleがあればキャッシュから取得し、無ければダウンロードする
  • キャッシュには保存可能容量や有効期限が存在する
    • Cacheing.maximumAvailableDiskSpace
    • Cacheing.expirationDelay
  • バージョンを上げても旧バージョンを削除しない
    • 現状はcrcが異なる場合、削除してDLし直す挙動を利用する感じに...
      • (私的メモ) これってUnity5.3以降でも効く? 動かない的な記事を見たことがあるので検証したい
  • キャッシュの処理はワーカースレッドで行う
  • 複数を同時に走らせるとOSの同時アクセス限界

キャッシュシステムを自作

  • WWWクラスはbytesアクセス用にメモリを確保していため、メモリを余計に使う

ベストプラクティス

同一の要素を大量に含んだPrefab

  • 単一のPrefabをInstantiateで複製した方が若干効率的

Resources

  • Resources/AssetBundleには直接参照しないアセットは含めない
  • Resourcesのアクセス数を減らす
  • Resourcesはプロトタイプ用と割り切る (unity社内のオススメ)
  • アプリにAssetBundleを含めてしまう
    • StreamingAsset等
    • ただしAndroidはapk(zip)内にStremingAssetsがあり、LoadFromCacheでアクセス出来ない
      • CacheOrDownloadや何らかの手で取り出す

AssetBundleの分割

  • 論理エンティティによる分割
    • DLC等に使いやすいフォーマット
    • レイアウトやUI,キャラクター
    • 共有する背景のモデルやテクスチャ
  • タイプのグループによる分割
    • 同タイプのアセット(Texture/Audio)を1つにまとめる
    • 数が少なく、ローカルから接続するなら基本繫ぎっぱなしでOK
  • 同時コンテンツのグルーピングによる分割
    • SceneをAssetBundle化しておく使い方
    • 何度も使用するアセットは分割する
    • 多少のアセットの重複は気にしない
  • 1Asset1AssetBundle
    • カードゲーム等で有利
    • 依存関係の解決はスクリプトで行う
    • 5.3未満でAssetBundleを使用する上での最適解
      • LZMAが大きなサイズで極端に遅くなるため
  • 1つに拘らず、ケースバイケースで使い分ける

依存先AssetBundleはUnloadしない

AssetBundleはどの圧縮を使うべきか

  • ロード時間: 非圧縮 > LZ4
  • ビルド時間 非圧縮
  • アプリサイズ: LZMA
  • 使用メモリが問題: LA4 or 比圧縮
  • 通信時間: LZMA (CacheOrDownload) / 自前でzip圧縮

【Unity/uGUI】ボタンを長押しした際の挙動を実装する

Unityメモ
  • uGUIのボタンを一定時間押した際に何かしらの挙動を起こしたかった
  • 取り急ぎ以下のパターンの実装にて実装が可能そう
    • (1) 本家 UI.Buttonのコードを拝借しつつ拡張する
    • (2) 本家 UI.Buttonを継承しつつ、必要な拡張要素を記述する
    • (3) 本家 UI.Buttonとは別のUnityEventの定義を記述する
  • 長押しのために複数スクリプトを都度アタッチするのはまどろっこしかったので(3)はパス
    • RequireComponent(typeof(Button)) すればいいじゃんと言われそう
  • (1)は本家のコードを持って来てoverrideする感じになる(?)
    • Unityのバージョンが変更されてUI周りに変更が生じた時に追従できなくなるのでパス
  • 今回は(2)のケースでの記述を試みる

コード

gist.github.com gist.github.com

挙動

f:id:lycoris102:20160725231029g:plain

メモ

  • OnPointer(Down|Up)UI .Selectable に生えている関数
    • SelectableEventSystem からの入力を受け取りイベントを発火する
    • Buttonで定義されたイベントを base.XXX で呼び出しつつ、独自の内容を定義
  • UnityEvent を使うことで独自のコールバック機構が実装可能
    • UnityEvent.AddListener でコールバックメソッドを追加
    • UnityEvent.Invoke が実行されるタイミングでコールバックメソッドが実行される

余談

  • Inspector上でイベント登録出来る様にするにはEditor拡張が必要
    • 本家を参考にすると良さそう
  • TouchScriptInput.Touches等のアセット使うと車輪の再発明しなくて良さそう
  • UniRxのCreating custom triggersのサンプルが長押しなので使える場合は使うと良さそう

github.com

参考

westhillapps.blog.jp albacrow597.blog.fc2.com teratail.com

【Unity/AssetStore】AlphaMaskを使ってみた

Unityメモ
  • 7/15のアセットストアのセールで売っていたので購入
  • ちょうど下記のようなことが出来ないか考えていた

こんな感じのアセット

https://www.assetstore.unity3d.com/jp/#!/content/29764

  • 任意のSprite/uGUI要素、およびその集合に対してMaskを掛ける事が出来る
  • ShaderおよびMasterialを要素を適応させるためのScript、サンプルシーンが内包されている

使い方

f:id:lycoris102:20160722085903g:plain

  • (1) 親となるGameObjectを生成する
  • (2) 子としてMaskを適応したいSpriteを配置する
    • uGUIの場合も使用可能で、Imageを使用する
  • (3) 子としてAlphaMask内にあるMaskをアタッチしたGameObjectを配置する
    • Attach時にMaskRendererがAddComponentされる
  • (4) MaskMaterialに任意のTextureを指定する
  • (5) MaskComponentやscale等をよしなにごにょる
  • (6) MaskComponent内の [Apply Mask to Siblings in Hierarchy] を押す
  • (7) 自動的にSprite (image) のMaterialに対してMaskMaterialが適応される
    • Maskと同居しているGameObjectが子要素を持つ場合、子にも適応される

MaskComponent

  • MaskMappingWorldAxis
  • マスクの軸 / 通常はカメラの向きと同方向に設定する
  • InvertAxis
  • 反転させた軸に対してマスクを適応させる時に設定する
  • ClampAlpha(Horizontally|Vertically)
    • Textureの設定がClampではなくRepeatになっているケースにおいて、任意の方向にClamp扱いにする
  • ClampingBorder
    • 上記のClampの設定が有効の場合、境界を調整する事が出来る
  • UseMaskAlphaChannel
    • アルファチャンネルを有効にする
  • DisplayMask
    • MaskのTextureを描画する (確認用)
  • Apply Mask to Siblings in Hierarchy
    • 上記の設定を反映させる

f:id:lycoris102:20160722085959g:plain

雑感

  • 複数コンポーネントに対しMask掛ける事が出来るのが良い
  • Shader書けない民は一旦これで、という感じ
  • Sample内に含まれている「ロゴにスペキュラ掛ける」みたいな用法も出来て面白い
  • セールなので勢いで購入したけど類似アセット結構ありそうなのが懸念...!!