ぷらこあ

ゆるふわゲームクリエイター / イベントオーガナイザーを目指してます

Unite2015Tokyo Day2メモ

  • ざっくり自分用メモ
    • uGUIパフォーマンスの話が特に参考になった
    • 英語や上級者向けの公演全然付いて行けなかった…
    • Urban Coaster死ぬかと思った…

Unityパフォーマンスチューニング

ゲーム作成時のオプティマイズ

  • FPS (30-60なら滑らか)

    • CPU: ゲーム挙動/物理挙
    • GPU: shader/drawwcall
  • Hiccups

    • Spikes (物理挙動のリビルド等)
    • Loading

Unityプロファイラ

  • プロファイラで見れるもの
    • CPU/GPU/Audio/Physics/Memory usage
  • Unity5はパーソナル版で誰でも使える様になってるよ

CPUプロファイラ

  • 時間がかかっている順に表示
  • ゲームが実際に動いている最中に表示出来る

CPUプロファイラ: タイムライン

  • 何が起きたか視覚的に把握出来る
  • 同期/何かが起きたときに中断が起きているなど

メモリプロファイラ

  • ヒープ用にかなりメモリを割り当てている
  • 詳細を見ることが出来るが負荷的にスナップショット的な活用が望ましい

LOADING DATA

  • データ種別
    • シーン
    • シーンが依存するアセット
    • リソース/ストリーミングアセット/WWW
  • 自分の好きなローディングパイプラインを作ることを可能

  • 非同期ローディング

    • Application.LoadLevel *Async
    • AssetBundle.Load* Async
    • 5.0: Resources.LoadAsync
    • Batch size still matters
    • 5.0: texture continue to load in background
  • シーンローディング: メモリの消費

    • 現在のシーンは新しいシーンがreadyになるまで取り込まれない
    • アセットに関しても同様
    • メモリスパイクが発生するときの対処
      • 空のシーン(loading)を用意し、古いシーンをpargeしてから新しいシーンを読み込む等
  • ローディング: シリアライゼイション

    • Unityのシリアライゼイションを高速である
    • 違うバージョンに関しても互換性がある
    • 元々はリフレクションをその場でやっていたので遅かった
      • Unity4.5以降で改善
    • 将来
    • ブロブ化する (mecanimが使用しているやり方)
  • ローディング: アセットバンドル

    • プレイヤーの再是の最適化
    • build timeの最適化
    • ローカル/Webサーバから高速データ取得
      • WWW.LoadFromCacheOrDownLoad();
      • 全てのデータをバンドルすることが出来る (WWW特有問題の解決)
  • UNLOADING

    • オブジェクトの削除作業のスレッド上での実施
    • Resources.unloadUnusedAssets / GC.Collect
    • 安全な仕組みが無い
      • 今使用しているアセットか否かを判断出来ない
  • GC パフォーマンス

    • GC.Collect によりスパイクが発生する
  • GC オプティマイゼイション

    • GCが起動しない様にする
    • GCを高速で動く様にする
    • リファレンスが密にパッキングするような設計が必要
    • classの代わりにstractにする (インラインになる)
  • REDUCE GC WORK

    • レファレンスするようなデータをパックする
    • メモリの移動量を減らす / メモリを消去する
  • RESULTS

    • GC for array length of 1,000,000 charactoets on MBP
    • Class with randomized memory locations: 35ms
    • Class with linear memory: 20ms
    • Struncts: 10ms
    • Strunct and no String: 0.18ms
  • MINIMIZING MANAGED ALLOCATIONS

    • Reuse temporary buffers
    • Allocate pools of reusable
    • Use structs instead of classed
    • OnGUIは使用しないで new UI System を使ってね

プール

  • GC chrun

    • Instantiate/Destroy overhead
  • プール方法

    • プールの中ではdictionaryは使わない、Listを使うのが良い
    • 事前に暖めておく
    • プールの中のオブジェクト
      • 使用するときにSetActive(true)
      • 画面外へ置いておく
    • 5.0 においては Instantiarion / actibvation のパフォーマンスが50%程度改善している
  • 一般的なパフォーマンス関連のコツ

    • GameObject.Find避けよう
      • 結果をcacheするのもいいだろう
      • GetComponent使うのもいいだろう
      • direct linkage or managers
    • 過剰なトランジションを避ける
    • 全てMonoBehaviorを使う必要が無い / 通常のC#Classを使う
    • GameObjectも必ず使う必要がない (transform等データ格納しておくといいだろう)
    • 階層の深い子オブジェクトの transhorm の変更は親に影響が及ぼすので避ける

将来的に

  • メインスレッドから処理をどんどん剥がして行く
  • ストリーミングの改善/最適化
  • GCを静的に
  • transformの階層化に対する負荷を下げる
  • 更ならプロファイリングツールを!

まとめ

  • 非同期ローディングしよう
  • メモリの利用パターンを意識しよう
  • データをレイアウトする際にゲームパフォーマンスを意識する
  • オブジェクトをキャッシュして再利用しよう
  • パフォーマンスの最適化のためにプロファイラを見よう

Unityのシーンを紐解き把握するには

  • 今回話す内容
    • プロジェクトをサクっと読む方法
  • 何故こんなことが必要になったのか
    • バグレポートの構造解析
    • サンプルシーンの解析
    • コード記載するの面倒
      • 昔作ったプロジェクトが読めない
      • シーンの構造やメタデータをごにょごにょする話し

シーン構築のルールを確認

  • プロジェクト>シーン>オブジェクト>コンポーネント
  • プロジェクト
  • コンポーネントはコールバックで動作する
    • 始点はコールバックから
    • Start/Update/FixedUpdate/OnCollisionEnter…
  • コンポーネント動かない条件
    • 非アクティブなオブジェクトは動かない
    • 参照もコールバックも無いと動かない
    • コンポーネントを持たないと動かない
  • オブジェクトへのメッセージ
  • コンポーネントへのメッセージ
    • event/UnityEvent/StartCoroutine
  • 参照手法例
    • GetComponent
    • GetComponentInChild/Parent
    • FindWithType/FindWithTag
  • オブジェクトを作る方法
  • オブジェクトの元を得る方法

ルールを基に情報を集める

  • コンポーネントの呼び出しの把握
    • どのコンポーネントがいつ動くのかが大事
      • コールバック (OnCollider/OnTrigger/OnMouse/SendMessage…) を持つか?
      • ランタイムで増減する
  • コンポーネントの列挙

    • コンポーネント一覧からからコールバック一覧を取得するエディタ拡張作るハック
  • IEventSystemHandler

    • IEventSystemHandlerを継承している場合はインスペクタで確認出来る
    • e.g) uGUI>button>InterceptedEvent
  • 文字列のメソッドコール

    • 任意のメソッド名で呼ばれる (AnimationEvent/SendMessage/EventSystem)
    • コールバックとして計上しておく
  • コンポーネント参照元の確認

    • 無理
    • オブジェクトを消したときのリスクが不明
    • 参照情報を元に被参照リストを作る
  • 三章先の座標の確認

  • handlerを使って矢印を貼ると紐付けを確認しやすい
  • 量が多すぎると混乱するのでストリップする仕組み

  • 以下を参照リストから削除する

    • オブジェクトの親子関係で構築した参照
    • オブジェクト内で自己完結している参照
    • 選択中のオブジェクト内での参照
  • どこからも参照されないオブジェクト

コンポーネントの依存関係

  • Monodevelopで検索 > シーンで使用されているかを特定出来ない
  • エディタ拡張書く

    • MonoScriptでMonobehaviourのコードを取得する
  • 参照関係をグラフ化

  • Model

どういうオブジェクトか

  • マネージャー系オブジェクト

    • オブジェクト総数が1つでコンポーネント的な参照が多い場合
    • 大抵シングルトン
    • 外部からのメッセージを受けて動作する方が多い
  • ギミック系オブジェクト

    • コライダー等で自己完結するオブジェクト
    • Playerへの参照があるかないかくらい
    • トリガーやアクションをマネージャーに通知するタイプも居る
    • ステージ系のゲームにありがち
  • Updateで頑張るドン

    • メインオブジェクト
    • 多くの参照を集めている対応でおおい
    • Updateやコルーチンで自発的に動く
    • コードを見れば処理が判る

そのオブジェクトがどうやって参照されるか

  • 参照先の状態 (パラメータ) を注目する
  • 自分で動くか管理されるかの似たく
    • 被参照が一つの場合管理されている可能性が多い
    • 被参照が多い場合、自分で動く可能性が多い
  • タグを保つ場合、FindWithTagが使われてる

    • 親子関係で検索される
    • 名前で検索される場合もある
  • 対マネージャー (シングルトン)

  • 何故参照を持つのか

    • 検索のキャッシュ? …
    • 判るような命名規則付けるのが一番ですね!

そのオブジェクトはどこから持って来たのか?

  • 生成コードの検索

    • シーン無いに最初から配置されているか?
    • シーン内でオブジェクトを生成するコードは検索する必要がある
      • Instantiate
  • 該当のプレハブを持つシーンを探す

    • LoadLevelAditiの場合、ロードするシーンを探す
    • 生成物がプレハブの場合、事前にシーン内の参照情報を保持しておく
    • ReferenceViewer (エディタ拡張マニアクス)
  • YAML変換してgrepしよう

    • UnityYAMLMergeが入って (smart merge) シーンやプレハブのマージが可能に

このシーンはどんな構造なのか

  • 実行時にシーンが持つコンポーネントを知る
  • オブジェクトの役割と参照関係を知る
  • オブジェクトが作られる条件を知る
  • コンポーネントの動くタイミングを知る
  • オブジェクトの動きを把握すると大体シーンの構造が判る

UnityGUI開発至難 (FAST UI BEST PRACTICE)

  • パフォーマンスの高いIF
  • Unity5のフレームデバッガ
  • バッチング処理

バッチ処理の基礎

  • 決まった順番でメッシュがレンダリングされる
  • 決まった筆が無いと描画されない (プロパティ毎に)
  • ステート変更に時間がかかる
  • 同じステートのメッシュは1つのバッチと捉えて、1回のdrawcallで描画される
  • オブジェクトをバッチにグルーピングする処理をバッチングという
  • テクスチャの変更は速い / マテリアルの変更は遅い
  • マテリアルの変更
    • シェーダの変更が入るのが遅い原因 (リビルドが挟まる)
    • SetPassCallはこの遅い変更のことを指す
  • Statuisticsを見るとバッチ数とSetPassCallが表示される

バッチング

  • バッチング vs バッチングしないケース
    • どう見た目が異なるかデモ
  • バッチをどのように計算されているか
    • 内部ルールで決まってくる
    • マテリアル変更/マテリアルプロパティが共有された/要素がオーバーラップされた場合

FrameDebuger

  • 何がバッチとしてまとめられるか見られる
    • Window>FrameDebuger
    • どのシェーダ/マテリアルプロパティ/頂点数
  • Canvas単位でBatch処理を行う
    • CanvasInCanvas は独自のバッチを生成
  • バッチ生成のルール

    1. 同じマテリアルである必要がある
    2. バッチが切れるのはテクスチャ/マテリアルが異なる/変更する場合
    3. エレメントが重複配置している場合はバッチが切れる場合はある
    4. バッチングソーティングの話
      • UISystem
    5. (Unity5.2) Canvasからの同一平面性が壊れる場合、ソーティングを切る
    6. 例えばzが異なる場合バッチンググループによって表示が崩れてしまうので
  • マテリアルの変更は最小限に抑える

    • 同じマテリアルを使う必要がある
  • 5.2: テキストとノーマルUI要素は同一のマテリアルとして扱う

  • テクスチャー

    • UIの中での変更を抑えよう
    • spriteAtlasなど一つのテクスチャーにパッキング居ておく必要がある
    • Textは動的変更なのでテクスチャは異なる = バッチングが異なる

サンプル

  • 25のDrawCallが発生するシーン
    • Canvasから離れている > 同じz座標へ
    • Textureが異なる > SpriteAtlas > パッキングタグを付けてSpriteEditorにてパックする!!

CanvasRenderer

  • スクリーン上に現れる全てのUI要素が所持する
    • texture / mesh / color を保持する
    • Text / Image を使うときには必ず付与される
  • 変更するとバッチングのし直しが発生する
    • バッチの計算し直し
      • マテリアル/テクスチャーの変更
      • 遅くも速くもないので出来れば避けたい
    • 階層の順番が変更されたとき
      • 親に対して子が追加された/子の順番が変更された
        • 遅いのでなるべく起きない様にしたいよね

PixelPerfect

  • スナップ処理
    • 矩形と大きさを合わせる処理
    • 遅いので出来るだけ使わない方にするのが良い
    • オブジェクトを動かすときだけON、動かし終わったらOFFにするなど工夫出来る
  • ScreenSpaceCanvasのみ機能する

Custom UI Elements

  • 独自のグラフィックを作成する
  • 独自のカスタムレイアウトを作成する

  • ICanvasElementという概念

    • いくつかのメソッドを付与する必要なある > Rebuild
    • rebuild callbacks
      • Prelayout
      • Layout
      • PostLayout
      • PreRender
      • LatePreRender
  • イベントのトリガー
    • CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild();
    • CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild();
  • イベント処理のタイミング
    • [FIXME] スライド公開されたら見る
  • リビルドがトリガーされる例
    • OnRectTransformDimensionsChange
    • OnTransformParentChanged
    • OnTransformChildrenChanged (for layout group)
    • OnCanvasGroupChanged
  • ショートカットも用意されてるよ
    • 以下を継承する
      • Graphic
      • Selectavle
      • LayoutGroup

More Information

  • セッション: ロードマップ
  • 検索: フレームデバッガー
  • 検索: スプライトパッカー
  • コード: Unity UI bitbucket

白猫プロジェクトの裏側

開発フロー

  • プロトタイプ 2-3名 1ヶ月
  • アルファ版 10-13名 4ヶ月 (アクションの基礎が楽しめる完成度)
  • ベータ版 15-18名 4ヶ月 (ゲームサイクルが回せる程度)
  • リリース

  • 開発特徴

メモリ/パフォーマンス

  • 基本はキャッシュ

    • e.g) Instantiate
    • ダメージ表記: 処理落ち
      • Before: if (action.hit) { Instantiate(DamageNumber); }
      • CachedObjectManeger.Instance.OreCache(obj, num); // 生成個数
      • CachedObjectManager.Instance.Borrow(obj);
    • AudioClipキャッシュ
    • エフェクトのキャッシュ
      • 今回は断念
      • ParticleSystemをActiveにしたときに別の座標に表示されることがあった
      • ParticleSystem以外の制御も必要だった
      • キャッシュを前提にしたルール大事
  • GPU側のボトルネック

    • マップ画面で処理落ち
    • 海をキラキラさせるシェーダ
    • フラグメントシェーダーで波のアニメーション計算していた (sin使用)
      • バーテックスシェーダへ波の計算を移行した
      • 使っていない変数に計算値をセットしてフラグメントシェーダに渡す
    • シェーダーの一括変換
      • 必要の無いカラー計算をしているシェーダーを一括置換
      • PC用のシェーダー使用も一括置換
  • 起動時間の拘束か

    • タイトル出るまで Androidで25秒 / iOSで1分
    • AssetBundleは現在約3万種類 Caching.ready になるまでが遅い (12万ファイル数程度)
    • 独自のキャッシュシステムを作る
      • Cacheing.ready を待たずに起動速度を速く
      • WWW.LoadFromChaceOrDownLoad 使わない (コロプラのゲームではもう使ってないよ)
      • AssetsBundleのバージョン管理が出来る必要がある
  • 自前で用意したキャッシュシステム

    1. 起動時にAssetBersionListをダウンロード (path_versionNumber の形式)
    2. PlayerPrefに保存されているversionを比較
    3. 更新されていたらダウンロード
    4. ストレージへの書き込み
    5. PLayerPrefabsに新しいバージョンを入れる
    6. 1分から8秒に
  • ファイル読み込む数の問題

    • iOSで開けるファイル数は256個 (255個?)
    • カットシーンで大量のAssetsBundleをダウンロードしていた
    • バックグランドでのダウンロードを増やしたせいで引っかかった
      • 問題点として原因が特定しにくい
    • 1ファイルずつCloseしOnCompleteで何も行わない様にして解決

メモリとプラグラムサイズの最適化

  • 不必要なdll, コードの削除

    • AssetsStoreで購入した者のサンプル等は削除するかEditorフォルダに入れる
    • 同様の機能を行う者が既に入っているのにdllをリンクしてしまっているケースがあった
    • .jsがあると Boo.lang.dll がリンクされるので必要なければ消しておく (アセット内注意)
  • Stripinglevelの変更

    • UnityEngine以下の必要なコンポーネントまでStripされることがあった
    • Linkerシーンを作成
      • 必要なコンポーネントを持つオブジェクトをシーンに配置する (使っておく)
      • シーンの作成には自動でコンポーネントを抽出してGameObjectを生成
      • AwakeでルートをDestryoy
  • AssetsBundleのメモリリークの繻子エイ

    • 明示的にReleaseする必要がある管理方式
    • 特定のOwnerがDestroyされてAssetBundleが開放されない
      • Xcodeを使って調査した
      • FrameDebuggerでVRAMの中身をチェックして破棄されてなければならないテクスチャを調査

今後の改題

  • CharactorController使ってる
    • 重い / Unity5で挙動変わってる
  • UIの構築用ではない軽量な2Dアセットを探す / 作る
  • キャッシュで来て大量に表示出来るエフェクトシステム
  • ダメージパケットなど大量に送信する際のバッチング

リアルタイムサーバー

サーバシステム構成

  • [FIXME] そのうち画像上がるだろう

リアルタイム通信

  • host: 共闘で必要になるどー来データのマスターデータを持つ
    • ボスデータ
    • 自キャラ
    • ステージギミック
  • guest
    • 自キャラ
    • hostユーザからデータを貰う
  • (雑魚キャラは動きは同期していなくて生死のみ管理)

  • 通信保証データ

    • データが受け取れないと進行布野になるような重要度の高いデータは受け取り済みであることをコールバックする
    • コールバックがなかったら最初からリトライ
    • 接続の有無は定期的にpingを送信してサーバ側でpingがないユーザは切断判断する

クライアントサーバ注意点

  • クライアントのサーバのデータ不一致
    • サーバのデータをコピーしてクライアントが持つ場合不一致が生じる
    • リクエスト成功、レスポンスが無い場合
    • モバイルは通信環境によって接続される
  • データが不一致だとサーバ側でエラーが発生する
  • トークンを付与したリクエストを送る
    • トークンをキャッシュして、通信切れた場合に再度トークンを付与してリクエストを送る
  • DBの最大値を入れてテストする
    • MySQLではBigintで管理していた値をクライアントでは int (System.Int32) で管理して事故発生
    • DB側をintに合わせて対応した…

Unityで音を制す

CRIミドルウェア

  • CRI ADX2
  • CRI Sofdec2
  • ファイルマジックPRO

ゲームサウンドに求められるもの

  • データサイズを小さく
  • メモリ使用量を小さく
  • 気持ちのいいサウンド
    • 切り替えの滑らかさ
  • ゲーム情報としてのサウンド
    • ユーザ通知を判りやすく
  • キャラや世界の表現
    • ボイスや環境音/賑やかさ

ADX2が変えるワークフロー

圧縮コーデック

  • ADX/HCA/HCA-MX(これが一番ゲーム向け/多数再生向け)
    • 極端に圧縮率が劇的に変わる訳ではないので注意

イントロつきループ再生

  • ループポイント付き波形の圧縮/再生に対応

事例紹介

  • 乖離性ミリオンアーサー

    • SocialGameInfo 採用事例のインタビュー
    • 台詞再生時のBGM音量調整
    • BGMは微妙な増減程度 (BGM聞きたいユーザ向け)
    • 企画の方が音の調整していた
    • オプションの音量設定
      • データにカテゴリー情報を入れて、カテゴリへの音量指定
    • サウンドパックファイル: 1音1パックファイルに近い > ファイルマジックによるパック
    • ストーリーの音量は章毎に別途ダウンロード
    • チュートリアルプレイ中に追加データのダウンロード
  • 七つの大罪 ポケットの中の騎士団

    • 5人のボイスが一斉に
    • ストリームで再生しているので普通は合わせるの大変
    • プログラム側で再生紙
    • BGM26曲/SE25種/ボイス1800種
  • ファントムオブキル

    • フィールド曲とバトル曲のつなぎ込みに工夫
    • 同期再生して音量をインタラクティブに変化させてる
    • ボイスの圧縮 5800種 1500MB > 150MB
    • キャラボイスはキャラ毎 1キャラ100ボイスぐらい
    • 追加のBGMのみダウンロードしてくる

サウンド制作から組み込みまで

  • 通常のワークフロー
  • ADX2入れた場合
    1. 演出設定とプラグラムの分離
    2. サウンド担当者が演出設定まで担当する

開発支援

  • リアルタイムサウンド変化
  • 実行中のアプリで直接サウンド調整
  • 音ゲーの楽曲と踏めん表示のタイミング合わせを支援
    • プログラムによる楽曲再生とスピーカーからの発音の遅延時間の通知

Unityを利用したスマートフォン向けゲームアプリ開発へのアプローチ方法

会社紹介

  • SummerTimeStudio
  • 沖縄の会社だよ / 沖縄出身のエンジニアが多いよ
  • ココイチ戦争
  • 開発合宿

開発スタイルと特徴

  • スマートフォン/タブレットに特化

    • コンシューマー向けの3D技術活用
    • 全世界のユーザへスピーディにクオリティの高い3Dゲームの提供可
  • コミュニケーションン工藤開発

    • 会議フォーマットの撤廃
    • 問題点/疑問点をその場で解決する
    • 話した内容に付いてはすぐにメモして共有
    • 全員がフラットな立場で話し合う
      • 一人一人が主体性を持ったものづくり
    • 各プロジェクトに3名のメンバーをアサイン
  • 小数精鋭のスポード重視

    • プログラマ1名
    • 3Dアーティスト1名
    • 2Dアーティスト1名
    • プロジェクトの規模や開発フェーズに応じて柔軟に対応
  • 最小限のルールで柔軟に変化

    • 時代の流れや環境の変化に合わせた最良を追求

開発環境の構築プロセス

  • サマータイムスタジオに入る前のこと

    • 3Dゲーム開発を0からいろりおと構築してみた
    • AndroidSDK/Eclipse
      • OpenGL/GynamicFont対応/3Dモデルフォーマット設計/Mayaのネイティブプラグイン
      • これは一人では無理だ
    • 3Dの開発には必要な者が多すぎる
      • そんな時に思い出したのがUnity
      • あっという間にプロトタイプ完成した
  • 今までは自分が作ることが多かった

    • 動く者を作るのは楽しんでやれる
    • 自分で作れば自由に最適化出来る
    • 過去のノウハウで何とか乗り切れてしまう
  • Unityをそのまま使ってみる

  • 実感したUnityの弱み (3.x当時)
    • スマホ/タブレット向けに最適化が進んでいない印象
    • LoadLebelで画面が止まってしまう / 特定の端末で不具合
    • 2D描画弱い
  • ただしプラグイン/アセットで補完

    • ユーザが多く問題が解決されている
  • スマホ/タブレット向けの効率化

    • Unityの中で起きている問題は対応難しい
      • その機能を避ける
      • 実際はUnityのバージョンアップに伴い解決が進んで行った感じ
    • マルチプラットフォーム対応の効率化
    • Android/iOSでの差異を九州
    • アプリ内課金の実装
    • アチーブメント、リーダーボードの実装
    • モバイル向け広告の実装 … etc
  • タイトル固有部分以外の効率化と安定化

STFramework紹介

  • STFramework.Initialize()を呼び出す
  • InputManager
    • input.GetTouch() はエディタ上で動かない
    • UnityRemoteで動作進化あった
    • エディタ上でも気が売るに動作確認したかった
    • マウス入力をタップと同じフォーマットにして返すだけ
      • マルチタップはUnityRemote必須
  • SceneManager
    • Scene切り替えをスムーズにしたい
    • Sceneの遷移や管理を自動化
      • 初期化、破棄処理をコルーチンで実装
      • シーン遷移におけるコールバック処理
      • Scene毎にクラス定義が必要
  • UIManager & UI Components
  • PurchaseManager
  • XMLImporter
    • データテーブルの管理はベタよりExcelなどを使った方がよい
    • Excel -> XML出力して読み込む
    • ScriptableObjectに対応して余計なコンポーネントの作成を不要に
    • 一定の規則に従い暮らす/XMLを作成することでXMLインポートとデータの更新を自動化
  • その他

今後の課題

  • Unity5への対応・最適化
  • エディタ拡張の整備