DCEXPO2016 に行ってきました
- 技術的なことは Qiita に、成果物やイベントについてはブログにと出し分けを始めました。
- ストックよろしくお願いします (宣伝)
- 久しぶりのブログです。DCEXPO2016の事前予約デー(10/27)にお邪魔して来たのでざっくり感想。
DCEXPO2016とは?
DCEXPO2016 | コンテンツ技術をテーマとした国際イベント
- デジタルコンテンツEXPOのこと
- コンピュータグラフィクスや3D、VR、インターフェースに関するイベント
- コンテンツが先行しているので技術詳しくなくても楽しめる
最初に感想
- 「技術が魅力的なコンテンツを支える」ということを身近なに体験できるイベントだった
- (自分の職能的にVR/ARとかモーショントラッキングとかあまり強い縁は無いのだけど) 最新鋭のエンタメはそういうの使ってより現実と空想の垣根を超えたコンテンツを (普及率はともかく) 僕達に押し付けてくれるので、見ておいて損は無かったなーというのは印象として持った
一部コンテンツを紹介
8k VRシアター
- HMDを使用しない新しいVR「8K:VR Theater」の第一作:サカナクションのVR映像「碧」を体験
- サカナクションのAOIのライブ/PVのような映像
- がまるで目の前に、ライブ会場の最前列で見ているように感じる
- (極端な言い方をすると動くフィギュアが目の前にある感覚)
- ヘッドマウントディスプレイ(HMD)無しで簡易的な3Dメガネのみで体験できる
- (これが重要で、簡単/疲れないっていうのがVRの垣根を限りなく下げている / VRよりもARに体験は近いかも)
- レーザーや立体音響等の演出が更に奥行きを表現している
- 音楽シーンやアニメ/演劇等の様々なシーンで活用出来そう
Qoncept 4D Tracker
Qoncept4DTracker 卓球のラリーのリアルタイム再現 #DCEXPO pic.twitter.com/bd1JeyGDcM
— 青木とと(ˊ꒳ˋ*) (@lycoris102) 2016年10月27日
- 卓球 (やバレーボール) のラリーのトラッキング/解析および再現
- スピン量や速度まで考慮したツール
- トレーニングへの活用から新しいスポーツ演出までカバー出来そう
Project Alice
ProjectAlice|VRFPSコンテンツで、銃にガジェットを(物理的に)アタッチするとショットが変わる的なやつ。銃やガジェットの位置をトラッキングしてて、HMD付けた状態でもちゃんとアタッチ出来たの凄かった。ちなみに二丁拳銃で左腕やや筋肉痛気味 #DCEXPO pic.twitter.com/31tX4Cjdyt
— 青木とと(ˊ꒳ˋ*) (@lycoris102) 2016年10月27日
- VRでFPSなコンテンツ
- 銃にアタッチメントを物理的に装着することで、ショットの種類が変わる
- 二丁拳銃も出来て、ファミ通Appの人も言ってるけど「男心をくすぐるコンテンツ」
- 他のプレイヤーの位置や銃、アタッチメントをHMD内でトラッキングしており、HMDを付けたままでも位置を認識できる
- HMD内で現実のものを認識出来る体験はリアリティを感じていいなぁ
- ただアタッチメントのほうが滅茶苦茶重くて、10分くらいの体験だったけど左腕筋肉痛に....
- 体験には整理券が必要だった
ハシラス | オムニジャンプ
ハシラス |オムニジャンプしたかったです!ツリバシコウカの方はVive付けさせてもらってちょっと見せてもらったけど、普通に踏み出せなかった…… (高所恐怖症) #DCEXPO pic.twitter.com/2Fy84gIXI2
— 青木とと(ˊ꒳ˋ*) (@lycoris102) 2016年10月27日
- 株式会社ハシラスという日本のVRコンテンツの先駆者によるコンテンツ
- サンシャインの上でやってるコンテンツもこの人達によるもの
- ガジェットを使った体験演出が得意なのだけど、今回はトランポリンを使ったコンテンツ
- 上から吊り下げたゴムを身体に装着し、跳躍力を高めながら減速を緩やかにし、安全性を担保しながら高く飛べる
- 更にHMD内では現実よりも高く飛んでいるように見せているため、脳はかなりの跳躍を感じる(らしい)
Unlimited Corridor (無限回廊)
- 自分の足でVR内を無限に歩き回れるシステム『無限回廊 – Unlimited Corridor』の動画が公開
- VR空間内で歩き回れる技術はViveが有名だがルームスケール (動ける領域) には限りがある
- しかし、このコンテンツは無限に歩き回れることが出来るのが特徴
- 「リダイレクテッド・ウォーキングとはHMDに表示する映像に補正を加えることで、実際には曲がって歩いているにもかかわらず、まっすぐ歩いていると感じさせる技術」
失禁体験装置
失禁体験整理券貰っちゃった/// pic.twitter.com/ioAbdR29B9
— 青木とと(ˊ꒳ˋ*) (@lycoris102) 2016年10月27日
- ニコニコ超会議辺りから非常にバズってたコンテンツ
- 「介護をする上で要介護者の気持ちが分かる」というコンセプトはかなり真面目で好感を抱く
- 最初に水を飲ませる導入、腹部への圧、生暖かさや濡れてる感触 (実際には濡れてない) 様々な仕掛けが再現率を非常に高めていた
その他色々
【Unity道場10】アセット運用ベストプラクティス に参加しました
参加しました!私的メモです(ˊ꒳ˋ*)
公開された資料
まとめ
- 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以下に全てのアセットを配置することで膨大な初期化処理が発生
- ルックアップテーブルも広がる
- 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に含まれる場合がある
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が呼ばれる
内部キャッシュシステム
- 指定のAssetBundleがあればキャッシュから取得し、無ければダウンロードする
- キャッシュには保存可能容量や有効期限が存在する
Cacheing.maximumAvailableDiskSpace
Cacheing.expirationDelay
- バージョンを上げても旧バージョンを削除しない
- 現状はcrcが異なる場合、削除してDLし直す挙動を利用する感じに…
- (私的メモ) これってUnity5.3以降でも効く? 動かない的な記事を見たことがあるので検証したい
- 現状はcrcが異なる場合、削除してDLし直す挙動を利用する感じに…
- キャッシュの処理はワーカースレッドで行う
- 複数を同時に走らせると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
- 1つに拘らず、ケースバイケースで使い分ける
依存先AssetBundleはUnloadしない
AssetBundleはどの圧縮を使うべきか
【Unity/uGUI】ボタンを長押しした際の挙動を実装する
- uGUIのボタンを一定時間押した際に何かしらの挙動を起こしたかった
- UI.Button コンポーネントは onClick (単一クリック) のみ有効
- 取り急ぎ以下のパターンの実装にて実装が可能そう
- (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
挙動
メモ
OnPointer(Down|Up)
はUI .Selectable
に生えている関数Selectable
はEventSystem
からの入力を受け取りイベントを発火する- Buttonで定義されたイベントを
base.XXX
で呼び出しつつ、独自の内容を定義
- UnityEvent を使うことで独自のコールバック機構が実装可能
UnityEvent.AddListener
でコールバックメソッドを追加UnityEvent.Invoke
が実行されるタイミングでコールバックメソッドが実行される
余談
- Inspector上でイベント登録出来る様にするにはEditor拡張が必要
- 本家を参考にすると良さそう
- TouchScriptやInput.Touches等のアセット使うと車輪の再発明しなくて良さそう
- UniRxのCreating custom triggersのサンプルが長押しなので使える場合は使うと良さそう