unityでいってみよう!

unityがチョットワカル位の人のブログ

Chocolateyでgradleの任意のバージョンをインストールしてみよう!

はじめに

前回はSDKMAN!でgradleの任意のバージョンをインストールしましたが、今回はWindowsで任意のバージョンをgradleをインストールしてみます。

unityletsgo.hatenablog.com

Chocolatey

Windows向けのHomebrewやSDKMAN!のようなアプリケーションマネージャーとしてChocolateyというアプリが一定の支持を受けているようです。

chocolatey.org

Chocolateyのインストール

  1. PowerShellを管理者権限で開きます
  2. 以下のコマンドを実行します。
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

最新のインストールコマンドはこちらをご確認下さい。

gradleのインストール

  1. PowerShellを管理者権限で開きます
  2. 以下のコマンドを実行します
choco install gradle

gradleを任意のバージョンへ切り替える

  1. PowerShellを管理者権限で開きます
  2. 以下のコマンドを実行します
choco upgrade gradle --version x.y.z

※x.y.zは任意のバージョン 例えば6.1.1へ切り替える場合は下記のコマンドとなります

choco upgrade gradle --allow-downgrade --version 6.1.1

なお、インストール時もバージョンの指定が出来るようですが、何故か上手く行きませんでした。

以上!

SDKMAN!でgradleの任意のバージョンをインストールしてみよう!

はじめに

UnityはAndroid Playerのビルドにgradleを使用しています。 Unity Editor上でバイナリの生成迄行う場合は、気にする必要がないのですが、インクリメンタルビルドを行う場合、Unityが内部で使用しているgradleとコマンドライン(Terminal)で使用するgradleのバージョンを合わせる必要があります。 unityletsgo.hatenablog.com

Unityが内部で使用しているgradleのバージョンを変更することはもちろん可能ですが、UnityはデフォルトのSDK・NDK・Gradle以外の組み合わせ以外に関しては非サポートです。その為、コマンドライン(Terminal)で使用するgradleのバージョンをUnityに合わせた方が良いでえしょう。

今回は、Unityが内部で使用しているものと同じバージョンのgradleをMacへインストールしようというお話しです。

Homebrew

brew.sh

自分はMacの開発環境構築に関しては全然詳しくないのですが、Mac上でのアプリケーションの管理といえばHomebrewを使っている人が多いと思います。Googleで検索するとHomebrewを使ったgradleのインストールの方法をよく見かけます。 以前は以下のようなコマンドでお手軽にバージョンを切り替えられた見たいですが、いつの頃かわかりませんが、Homebrewでは古いバージョンのアプリをインストール出来なくなったようです。(正確には出来なくもないがかなり面倒な手順を踏むようになった)

$ brew switch gradle 5.1.1

SDKMAN!

sdkman.io

gradleのホームページを読んでいると、SDKMAN!でのインストール方法が記載されていました。 どうやら、SDKMAN!を使用すればお手軽にgradleのバージョンを切り替えることが出来るようです。

SDKMAN!のインストール

Terminalで以下のコマンドを実行します。

$ curl -s "https://get.sdkman.io" | bash

インストール後、一度Terminalを閉じて、新規のTerminalを開き直せば、SDKMAN!が使用可能となります。

Gradleのインストール

sdk install gradle バージョン番号 で任意のバージョンのgradleをインストール出来ます。 例えばgradle 6.1.1をインストールする場合はTerminalで以下のコマンドを実行します。

$ sdk install gradle 6.1.1

各Unityが使用しているgradleのバージョンは下記の通りです。

Unity gradle
2018.4LTS 5.1.1
2019.4LTS 5.1.1
2020.3LTS 6.1.1
2021.2 6.1.1
2022.1 6.1.1

例えば、gradle 6.1.1をインストール済みだが、5.1.1を使用したい場合、以下のようにコマンドを入力すると

$ sdk install gradle 5.1.1 

Do you want gradle 5.1.1 to be set as default (Y/n) ?

と聞いてくるのでYを選択すれば、5.1.1が使用できるようになります。

また、再び 6.1.1に戻したい場合、下記のコマンドを実行します。

$ sdk default gradle 6.1.1

おまけ

sdk list gradle

インストール可能なgradleのバージョン一覧及びインストールされているものと、現在デフォルトになっているバージョンが確認出来ます

sdk uninstall gradle バージョン

gradleの特定のバージョンをアンインストールします。

Unityでインクリメントビルドを試してみよう!

はじめに

Unityで各プラットフォーム向けのバイナリ(実行ファイル)を作成する場合、大まかに下記の手順を踏みます。

  1. UnityEditorが各プラットフォーム向けにプロジェクトを出力する
  2. 各プラットフォーム向けの環境(Xcode,Android Studio)で上記プロジェクトをビルドする

今回説明するインクリメンタルビルドとは1のプロセスで変更のなりファイルを更新しないことで、2のプロセスの時間を短縮することを目的としています。

インクリメンタルビルドの手順

Androidプラットフォームの場合

f:id:kimukats:20211115172057p:plain

  1. Export Projectにチェックを入れ、Exportを実行します。

  2. プロジェクトの出力先として前回と同じディレクトリを選択します。

  3. Android Studioコマンドプロンプトから gradelw buildを実行してプロジェクトをビルドします。

iOSプラットフォームの場合

  1. Buildを実行します

  2. プロジェクトの出力先として前回と同じフォルダーを選択します。

  3. 下記のダイヤログが表示される為、Appendを選択します。  f:id:kimukats:20211115113522p:plain

  4. xcodeで出力されたプロジェクトをビルドします。

結果

インクリメンタルビルドを行うと1のプロセスの処理時間が延び、2のプロセスの処理時間が短くなります。 差分があったかどうかの判定に時間がかかっているということなのでしょう。

Unity Ediorからxcodeプロジェクトを出力する迄に掛かった時間

試行回数 Append Replace
1 44 seconds (44363 ms) 31 seconds (31496 ms)
2 40 seconds (39511 ms) 30 seconds (29881 ms)
3 32 seconds (32038 ms) 31 seconds (31099 ms)

xcodeでプロジェクトをビルドするのにかかった時間

Append Replace
14.9 sec 29.2 sec

AndroidプラットフォームではUnityEditorからGradleプロジェクトを出力する迄の時間にかかった時間に目立った変化はありませんでしたが、GradleプロジェクトからAPKをビルドする迄の時間が50 sec から 24 secへ半減しました。

今回の検証では、プロジェクトをまったく変更していない(差分が全くない)という状況下での検証でした。 差分が大きいと、プロジェクトをビルドするプロセスが長くなることが予想されます。

PlayerSettings.SetIncrementalIl2CppBuildの威力を計測してみよう!

はじめに

最近PlayerSettings.SetIncrementalIl2CppBuildというAPIがある事に気が付きました。スクリプトリファレンスには下記のように記載されています。

Sets a boolean flag indicating a IL2CPP supported BuildTargetGroup is going to use incremental builds.

これを素直にとらえるとIL2CPPでインクリメントビルドを有効にするか否かを指定する為のAPIのようです。 早速計測してインクリメントビルドでどの程度ビルドが早くなるのか確認したいと思います。

結論

結論を先に書いておきます。 PlayerSettings.SetIncrementalIl2CppBuildはIL2CPP のインクリメントビルドには何の影響もないデッドコードです。

計測環境

  • Unity Version : Unity2020.3.18f1
  • Project : LEGO Tutorial
  • プラットフォーム : Android
  • OS : Window10
  • CPU : Core(TM) i7-10875H CPU @ 2.30GHz
  • ストレージ : Samusung SSD 980PRO

計測方法

  1. Export Projectを有効にチェックをした状態でExportを実行
  2. PlayerSettings.SetIncrementalIl2CppBuild(BuildTargetGroup.Android,false)もしくはPlayerSettings.SetIncrementalIl2CppBuild(BuildTargetGroup.Android,true)を実行
  3. Script Only Buildにチェックを入れた状態で1と同じ出力先にExportを実行

2~3をそれぞれ3回づつ繰り返します。

結果

回数 インクリメントビルド無効時のビルド時間 インクリメントビルド有効時のビルド時間
1 42588[msec] 43241[msec]
2 42390[msec] 42558[msec]
3 42931[msec] 42723[msec]

・・・・変わらないですね。(*^-^)

考察

Unity公式マニュアルのIL2CPP ビルド時間の最適化には下記のように記載されています。

インクリメントビルドの使用

インクリメントビルドを行う場合、C++ コンパイラーは、最後にコンパイルしてから変更が加えられたファイルのみを再度コンパイルします。 インクリメントビルドを行うには、(ターゲットディレクトリを削除せずに) 以前のビルドロケーションと同じ場所にプロジェクトをビルドします。

この説明を読むと、「1度ビルドして同じ出力先へさらにビルドすればインクリメントビルド」となるとは記載されていますが、PlayerSettings.SetIncrementalIl2CppBuildに関しては一切の記載がありません。 どうやら、PlayerSettings.SetIncrementalIl2CppBuildによる指定は必要ないようです。

また、この後も色々検証してみた結果、以前のビルドロケーションと同じ場所にプロジェクトをビルドします。の部分ですが、少なくともAndroidプラットフォームにおけるビルドには関係なさそうです。

Androidプラットフォームに対するビルドを行う場合、Tempフォルダー以下に中間ファイルが出力されますが、その中間ファイルとの差分になるようです。 よって、UnityEditorでプロジェクトを開いて1度めのビルドは必ずフルビルド。2度目以降のビルドがインクリメントビルドになります。 但し、Export Projectの値を変更すると、インクリメントビルドでは無くフルビルドになるので注意が必要です。 (Export Projectにチェックを入れてビルドした後に、チェックを外してビルドするとフルビルドとなります。) また、これはExport Projectにチェックを入れていない、つまりプロジェクト形式ではなく、APKの形式で出力する場合にもインクリメンタルビルドが有効であることを示しています。

マニュアルに書かれている「1度ビルドして同じ出力先へさらにビルドすればインクリメントビルド」ですが、恐らくiOSプラットフォームの事を指しているのだと思われます。 元々IL2CPPのインクリメントビルドはiOSのみ先行して実装されており、ビルド時のターゲットディレクトリに以前と同じロケーションを指定すると「Warning Build folder already exists.Would you like to append or replace it?」というダイヤログが表示されていました。 この時、Appendを選択するとインクリメンタルビルドとなっていました。

その後、他のIL2CPP対応プラットフォームでもインクリメンタルビルドに対応したが、その後もマニュアルが更新されていないというのが現在の推測です。

Android以外のプラットフォームに関しは確認していませんが、もし違う結果になった場合はご連絡下さい。

情報お待ちしております。

AssetのImport時間を確認してみよう!

概要

Unity2020.2からImport Activity Windowが追加されました。 Import Activity Windowにはこれまでユーザーが取得することが出来なかったプロジェクトに関する様々な情報を取得することが出来ます。

Import Activity Windowを立ち上げてみよう!

Window > Analysis > Import Activity Windowから起動可能です。

それ以外にも適当なAsset選択後、右クリック > Import Activity Window 若しくはInspector WindowのTabの上で、右クリック > Import Activity Windowといった方法でWindowを開くことが出来ます。

f:id:kimukats:20211110141128p:plain

Overview

f:id:kimukats:20211110141311p:plain

Overviewでは依存されている数が多いAssetとインポート時間が長いAssetのベスト20?が表示されます。 これにより、インポートに時間がかかるAssetが分かるだけでなく、依存関係が多いことによって再インポートされる可能性が高くなるAssetを確認することが出来ます。

Assetリスト

インポートされたAssetの一覧が下記の内容でソート可能な状態で表示されます。 - インポートされた日時 - インポート時間

また、リストからAssetを選択するとOvervierの代わりに詳細情報が表示されます。

パラメータ 説明
GUID AssetのGUID
AssetSize Assetのファイルサイズ
Path プロジェクトフォルダーからのパス
Editor Revision インポートした時のUnity Editorのリビジョン
Timestamp インポートした日時
Duration インポートに掛かった時間
Reason for Import インポートした理由
Produced Files/Artifacts インポートした事によって生成されたLibrary以下のファイル
Dependencies 検索やソートが可能な依存関係のリスト

※再びOverviewを表示したい場合、Show Overviewをクリックすると表示されます。

以上

Android Logcatからsymbolicateしてみよう!

はじめに

アプリケーションがクラッシュした時、Logcatにはクラッシュがどのようなフローで発生したかを確認する為のスタックトレース情報が表示されます。

f:id:kimukats:20211004103945p:plain

しかしながら、スタックトレースは16進のアドレスとsoファイルの組み合わせで表示される為、このままでは人間には理解出来ません。 この情報を人間が読めるフォーマットへ変換することをシンボリケートと呼びます。 これまではAndroidプラットフォームの場合、シンボリケートを行う為にはコマンドラインのツールを駆使する必要がありちょっと難しい雰囲気がありましたが、Unityが公開しているAndroid Logcatから容易にシンボリケートが可能となりました。

Android Logcat

Android LogcatはUnityEditor上でLogcatの情報を表示する為パッケージですが、シンボリーケートやメモリ関連の情報を確認することが可能です。 Android Logcatはパッケージマネージャーからインストール可能です。

インストール

Window > Package ManagerからPackage Manager WIndowを起動します。

f:id:kimukats:20211004121348p:plain

Unity Registryを選択するとAndroid Logcatがリストアップされるので、Installボタンを押します。

Android Logcatでシンボリケートかいつのバージョンから使用出来るようになったか不明ですが、現在公開されている1.2.3以降であればシンボリケートを行うことが可能です。

シンボルファイルの入手

シンボルファイルはシンボリケートを行う為に必要な情報が詰まった必須ファイルです。 このファイルの一番簡単な取得方法はビルド時にCreate symbol.zipにチェックを入れてビルドを行うことです。

f:id:kimukats:20211004121857p:plain

このZIPファイルを展開し、Editor > Project Settings > Analysis > Android Logcat Settings からシンボルファイルを設定します。 画面右側の+ボタンを押してください。 Pickup Custom Location から展開したフォルダを指定します。

f:id:kimukats:20211004131624p:plain

シンボルファイルが無い場合、例えば他の人がビルドを行ったAPKをシンボリケートを行う場合、スタックトレースのこの部分に表示されている内容を参照してください。

f:id:kimukats:20211004124940p:plain

その設定に合わせてシンボルを選択します。

f:id:kimukats:20211004132515p:plain

この場合、libmain.soとlibunity.soに含まれるコードのシンボリケートは可能ですが、libil2cpp.soに含まれるコードに関してはシンボリケート出来ません。

シンボリケートしてみよう!

Window > Analayze > Andoroid LogcatからAndoroid Logcatを起動します。

f:id:kimukats:20211004133101p:plain

クラッシュが発生したらAndoroid LogcatのtoolsボタンからStacktrace Utilityを選択します。

f:id:kimukats:20211004133240p:plain

f:id:kimukats:20211004133345p:plain
Stacktrace Utility

Android Logcateではスタックトレースの情報が赤色で表示されているのそれをまるっとコピーしてStacktrace UtilityのOriginal Tab側にペーストします。

f:id:kimukats:20211004133640p:plain

画面右上のResolve Stacktracesボタンを押します。

f:id:kimukats:20211004133811p:plain

文字列の色が緑色になった部分がシンボリケートされたことによって変わった部分です。

f:id:kimukats:20211004134915g:plain

例えば#00 pc 000f2224の部分が、il2cpp::vm::GetReducedType(Il2CppClass const*) at C:\Program Files\Unity\Hub\Editor\2020.3.18f1\Editor\Data\il2cpp\libil2cpp\vm/Class.cpp:?となり、実際にどのクラスの何行目でクラッシュが発生したのかを確認することが出来ます。 赤色の部分はシンボリケート出来なかった部分です。libart.so not foundとなっており、これはシンボルファイルlibart.soが見つからなかったことを表します。libart.soを何処からか入手できれば、Pickup Custom Locationから指定を行うことでシンボリケートを行いことは可能な筈です。

まとめ

  • Android Logcatでスタックトレースのシンボリケートが可能
  • シンボリケートを行う為にはシンボルファイルが必要
  • シンボルファイルはビルド時にCreate symbol.zipにチェックを入れることで生成されるZIPファイルから入手する

以上

【2021年版】リフレッシュレートとフレームレートの関係性を確認してみよう!

はじめに

Unity2018以降のリフレッシュレートとフレームレートの関係性を簡単にまとめてみました。 尚、この情報はモバイル(Android,iOS)向けに限定したいます。PCや他のプラットフォームではまた事情が違うことに注意して下さい。

※現在手持ちのMacが故障中の為、Androidバイスでのみ検証を行っております。その為、iOSに関しては検証を飛ばしていることにご注意下さい。

Unity2018

[iOS]Screen.currentResolution.refreshRateの修正

Unity2017迄はiOSの Promotion technology(refresh rate 120Hz)を有効にしていた場合でも、Screen.currentResolution.refreshRateの値は60固定でしたが、Unity2018から正しいリフレッシュレートを返すように修正されました。

※Promotion technologyを有効にした場合、実際に120[Hz]で動作していたが、Screen.currentResolution.refreshRateの値は60[Hz]を返していたということになります。

Unity2019

[Android]Screen.currentResolution.refreshRateの修正

Unity2018迄はアプリケーション起動時にのみ取得している為、アプリケーション起動後にリフレッシュレートが変更されてもこの値が変化していませんでしたが、Unity2019からは、アプリケーション起動中にリフレッシュレートが変更された場合でもScreen.currentResolution.refreshRateは現在値を返すように修正されました。

[Android]Time.deltaTimeの返す値に補正が入った

Application.targetFrameRateに-1もしくは、リフレッシュレートを整数で割った値のいずれかを設定しない場合、指定したターゲットフレームレートより低い値で動作します。これはモバイル端末の画面のフリックが必ずVSYNCで行われるです。フレームレートとリフレッシュレートの同期が一致しない場合、たとえゲームループがリフレッシュレートよりも早く動作しても、画面の切り替えの部分で詰まりが生じる為です。より詳細な説明は下記の通りです。

https://developer.android.com/games/sdk/frame-pacing#submit_frames_as_quickly_as_the_rendering_api_allows

The display pipeline contains a queue of frames, typically of size 2, which fills up if the game is trying to present frames too quickly. With no more room in the queue, the game loop (or at least the rendering thread) is blocked by an OpenGL or Vulkan call. The game is then forced to wait for the display hardware to show a frame, and this back-pressure synchronizes the two components. This situation is known as buffer-stuffing or queue-stuffing. The renderer process doesn't realize what's going on, so framerate inconsistency gets worse. If the game samples input before the frame, input latency gets worse.

例えば、リフレッシュレートが90Hzで動作している端末においてApplication.targetFrameRate = 60;とすると、フレームレートが低下します。フレームレートは一般的にTime.deltaTimeから求めますが、ほぼ空のSceneを計測した場合、Unity2018までは、0.0166666...と固定の値を返していましたが、Unity2019からはこの値が一定の周期で(0.01~33.4[ms]の間で)ばらつくようになりました。 下記のグラフはQualitySettings.vSyncCount = 0,Application.targetFrameRate=60で動作するアプリケーションをリフレッシュレートを90[Hz]に設定したPixel4XLで実行した結果です。Time.deltaTimeの値を毎フレーム取得した結果が黄色のラインなのですが、周期的にばらつきがおきているが、平均値は16.6[ms]になっていることが確認できます。

Time.deltaTimeの周期的なバラつき

[Android] Optimized Frame Pacingの実装

https://unity.com/sites/default/files/styles/810_scale_width/public/2019-07/unity-platforms-android-frame-pacing.jpg?itok=Ao5Bg6E-

Frame Pacingの機能の一つとして、マルチリフレッシュレートと呼ばれる機能があります。 これは、Pixel4のようにデバイスのディスプレイが複数のリフレッシュレートに対応している場合、アプリケーションのターゲットフレームレートに合わせて、リフレッシュレートを変更してくれます。

例えば、リフレッシュレートが90Hzの端末でApplication.targetFrameRate = 60;とするとフレームレートが低下すると記載しましたが、Optimized Frame Pacingを有効にするとリフレッシュレートが60Hzに変更される為、フレームレートが低下することはありません。 但し、上げ方向に変化することはなく、OSの設定が60Hzの場合にApplication.targetFrameRate = 45;とした場合でもリフレッシュレートは60Hzのままとなります。

バイスのリフレッシュレートとマルチリフレッシュレートで対応するフレームレートは下記の通りです。

  • On 60 Hz devices: 60 FPS / 30 FPS / 20FPS
  • On 60 Hz + 90 Hz devices: 90 FPS / 60 FPS / 45 FPS / 30 FPS
  • On 60 Hz + 90 Hz + 120 Hz devices: 120 FPS / 90 FPS / 60 FPS / 45 FPS / 40 FPS / 30 FPS

https://developer.android.com/games/sdk/frame-pacing?hl=ja#multiple_refresh_rates

Unity2020の場合

Androidバイスでの検証結果としてUnity2019と比較して、変化は見られませんでしたが、こちらの動画によるとUnity2020からのTime.deltaTimeの精度について話されているのでモバイル端末にも実は変化があるのかもしれません。

youtu.be

Unity2021の場合

[Android ]QualitySettings.vSyncCountの仕様変更

AndroidプラットフォームにおいてもiOSプラットフォームと同様にQualitySettings.vSyncCountの値を無視するようになりました。 ※QualitySettings.vSyncCount = 0として扱われます。

これはiOSプラットフォームもそうですが、VSYNC待ちをしないではないという事に注意して下さい。 リフレッシュレートがターゲットフレームレートで割り切れる場合、これまで同様のVSYNC待ちを行います。

その為、Application.targetFrameRateにリフレッシュレートを整数で割った値以外を設定すると、FPSが不安定になります。

以上です。

関連記事

unityletsgo.hatenablog.com

unityletsgo.hatenablog.com