unityでいってみよう!

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

FixedUpdateが実行されないフレームがある原因について考えてみよう!

概要

「FixedUpdate()が実行されないフレームがあるのですが、何故ですか?」 この質問が定期的に発生するので、この問題について解説します。

結論

フレームレートとTime.fixedDeltaTimeが一致していない為に発生します。

解説

UnityはPlayerLoopの先頭で下記の処理を行います。

  1. 最後のフレームからの経過時間をdeltaTimeとする
  2. deltaTimeがTime.maximumeDeltaTimeより大きい場合、deltaTimeの値をTime.maximumDeltaTimeとする。
  3. Time.timeにdeltaTimeを加算する
  4. Time.fixedTime <= Time.timeの場合、Time.fixedTimeへTime.fixedDeltaTimeを加算しFixedUpdate()を実行し、再び4を繰り返す
  5. Update()を実行する

例えば、フレームレートが0.16666...(60fps)でTime.fixedTimeが初期値の0.02(50fps)、ここで説明を簡易化する為に処理落ち等が発生せず、毎フレーム0.016で処理が終わると仮定した場合、各フレームにおけるTime.time、Time.fixedTimeの値及びFixedUpdateが実行される回数は下記の表のとおりです。

Frame Time.time Time.fixedTime FixedUpdateが実行される回数
1 0.016 0.02 1
2 0.032 0.04 1
3 0.048 0.06 1
4 0.064 0.08 1
5 0.080 0.10 1
6 0.096 0.10 0 | Fixedupdateが実行されない
7 0.112 0.12 1
8 0.128 0.14 1

6フレーム目に注目して下さい。 5フレーム目のTime.fixedTimeの値に0.020を足すと0.120となり、Time.timeの0.096をオーバーしてしまう為、6フレーム目ではFixedUpdateが実行されません。 これがFixedUpdateが実行されない原因です。 FixedUpdateに関して一部で、「FixedUpdate = 毎フレーム必ず実行されるアップデート処理」という間違った認識が広まってしまっていることも理解の妨げになっていると思われます。

Time.fixedTimeの初期値は0.020となっていますが、フレームレートと同じ値、もしくはフレームレートを割り切れる値を設定する事をお勧めします。 一般的には0.016666...が良いでしょう。 処理落ち等の要因によってずれる可能性はありますがフレームレートが60fpsの場合1回、30fpsの場合は2回づつFixedUpdateが実行されます。

まとめ

  • FixedUpdateは毎フレーム必ず実行されることを保証されている訳ではない
  • Time.fixedTimeの値をフレームレートに合わせることによってフレームとのずれを減らすことが可能

関連記事

unityletsgo.hatenablog.com

いまさらのPhysicsによる当たり判定のもやもやを解説してみよう!

概要

これまで、普通にUnityを使用してきたが、Collider.isTrrigerやRigidbody.isKinematicっていまいちよく分からんと思っている人向けの解説です。

ColliderとRigidbody

Unityにおいて物理エンジンの対象となるGameObjectはColliderコンポーネントのみの場合とRigidbodyとColliderの両方のコンポーネントを持つ場合の2種類が存在し、さらにCollider.isTrrigerの値、Rigidbody.isKinematicの値で計6種類存在します。

静的コライダー

Colliderのみを持つGameObjectを静的コライダーと呼びます。 静的コライダーは他のオブジェクトに影響を及ぼすことはありますが、自身が影響を受けることはありません。 つまり静的コライダー同士で当たりが発生することはありません。 用途としては絶対に他のオブジェクトから影響を受けない建築物や地面等、圧倒的な質量を持つオブジェクトは静的コライダーとして割り当てらる可能性が高いです。静的コライダーのメリットとしては、物理エンジンの計算に及ぼすコストが比較的軽い点です。

静的トリガーコライダー

Collider.isTrrigerの値がtrueの静的コライダーです。 トリガーコライダーは相手に影響を及ぼすことも、自身が影響を受けることもありません。 他のオブジェクトでぶつかるということはなく、他のオブジェクトは静的トリガーコライダーを貫通します。 つまり範囲のみを示したオブジェクトです。 イメージとしてこの範囲に入ったらイベントが発生する等のマーカー的な用途で使用されるケースが多いと思います。

Rigidbodyコライダー(動的コライダー)

ColliderとRigidbody両方のコンポーネントを持つGameObjectです。 他のオブジェクトに影響を与え、他のオブジェクトから影響を受けます。 基本的に世の中に存在するものは全てRigidbodyコライダーである筈ですが・・・。

キネマティックRigidbodyコライダー

Rigidbody.isKinematicがtrueのRigidbodyコライダーです。 他のコライダーから影響を受けることはありません。(相手には影響を与えます) 静的コライダーと同じ性質を持つため、Game中の静的コライダーからRigidbodyコライダーに用途が変更する場合、Rigidbody.isKinematicで調整する。

Rigidbodyトリガーコライダー

他のオブジェクトに影響を与えることも受けることもありません。

キネマティックRigidbodyトリガー

他のオブジェクトに影響を与えることも受けることもありません。

当たりイベント

Collisionを持つオブジェクト同士が当たった場合OnTrigger系とOnCollision系の何れかのイベントが発生します。

OnCollision系

どちらのオブジェクトもCollider.isTrrigerの値がfalseである場合に発生します。 つまり、オブジェクト同士がぶつかった場合に発生するイベントです。

OnTrigger

オブジェクトのどちらか、もしくは両方のCollider.isTrrigerの値がtrueである場合に発生します。 オブジェクト同士がめり込んでいるもしくは内包された場合に発生するイベントです。

当たり判定マトリックス

静的コライダー Rigidbody コライダー キネマティック Rigidbody コライダー 静的 トリガーコライダー Rigidbody トリガーコライダー キネマティック Rigidbody トリガーコライダー
静的コライダー × OnCollision × × OnTrigger OnTrigger
Rigidbody コライダー OnCollision OnCollision OnCollision OnTrigger OnTrigger OnTrigger
キネマティック Rigidbody コライダー × OnCollision × OnTrigger OnTrigger OnTrigger
静的 トリガーコライダー × OnTrigger OnTrigger × OnTrigger OnTrigger
Rigidbody トリガーコライダー OnTrigger OnTrigger OnTrigger OnTrigger OnTrigger OnTrigger
キネマティック Rigidbody トリガーコライダー OnTrigger OnTrigger OnTrigger OnTrigger OnTrigger OnTrigger

例外

Meshコライダー

  • Meshコライダー同士の当たりは不可であるが、Convex が trueの場合、出来る場合もある?

まとめ

  • 静的(トリガー)コライダー同士では当たり判定は発生しない
  • 何れかのオブジェクトがトリガーコライダーの場合、OnTriggerイベントが発生する
  • どちらもトリガーコライダーではない場合、OnCollision イベントが発生する

Unity2022からのWebGLでのAssetBundleのキャッシュについて調べてみよう!

概要

Unity2022からWebGLプラットフォームではCachingクラスが非サポートとなりました。

 

何故?

ブラウザー自体がキャッシュを行う為、Unity側が独自にキャッシュを行う必要がない為のようです。ドキュメントに以下の記載が追加されていました。

 The Cache API is not supported in WebGL because AssetBundles are stored in the browser cache for the WebGL platform. 

 

補足

問題となりそうなのは、あるAssetBundleがキャッシュされているか否かの判別方法ですが、その判定方法はなさそうです。

 

docs.unity3d.com

WebGL Playerのマルチスレッド対応状況を調べてみよう!

概要

WebGLPlayerでマルチスレッドを有効にする為に下記の条件を全て満たす必要があります。

  • PlayerSettings.WebGL.threadsSupportをtrueに設定する
  • WebGLPlayerを出力するが実行するブラウザー側も WebAssembly threadsに対応している
  • マルチスレッドに対応しているUnityのバージョンでビルドする

但し、C#のレイヤーでマルチスレッドに対応している訳ではなく、ネイティブ言語のレイヤーでのサポートである事に注意して下さい。

WebAssembly threadsに対応しているブラウザーについて

ブラウザーの対応状況はこちらから確認可能です。メジャーなブラウザーは全て対応済みとなっています。

マルチスレッドに対応しているUnityのバージョン

全てのUnityのバージョンでマルチスレッドに対応している訳ではありません。 下記の表のとおり、ある期間においてWebGLが非対応となっています。

Unity 対応状況
2019.4 experimental
2020.1 ~ 2022.1 ×
2022.2 ~2023.1 experimental
2023.2 ~

実験的機能について

Unity2023.1迄はスクリプトリファレンスにexperimentalの記載がありましたが、Unity2023.2ではその記述が外れています。 WebGLでマルチスレッドを有効にする場合は、Unity2023.2以降を利用した方が良さそうです。

以上となります。

Unity2022以降でWebGLのMemorySizeを指定してみよう!

概要

以前、Unity2019以降向けの記事としてWebGLのMemorySizeの指定方法を投稿したのですが、Unity2022では設定方法が変更されていたので、改めてUnity2022以降向けの設定方法を記載します。

unityletsgo.hatenablog.com

設定方法

Player Settings > Publising settingsから設定を行います。

https://docs.unity3d.com/ja/2022.2/uploads/Main/WebGLBuilding-PublishingSettings.png

Initial Memory Size

WASM ヒープメモリの初期値を設定します。初期値は32MBです。 Unity2022以前は16MBの倍数で設定する必要があった為、最小値は16MBでしたが、Unity2022以降では16MBの倍数制限が無くなった為、それより小さい値を設定可能です。但し、7MB未満の値を指定した場合、Stack用の領域が確保出来ないという理由でビルドエラーになるいことを確認しています。つまり、Unity2022.2においてWASM ヒープメモリの最小値は7MBとなります。

Memory Growth Mode

WASMヒープメモリーが不足し、領域を拡張する必要がある場合、どのように拡張するかを指定することが出来ます。

None

この設定の場合、領域を拡張しません。つまりInitial Memory Sizeで指定した値が、WASMヒープの最大値となります。

Linear

Linear Memory Growth Stepで指定した、固定長で領域の拡張を行います。 Unity2022以前と互換性を保つのであれば、Memory Growth Mode:LinearLinear Memory Growth Step:16MBを指定することになります。

Geometric

Geometric Memory Growth StepGeometric Memory Growth Capから拡張する値を決定します。

Geometric Memory Growth Step

現在のヒープサイズにこの値をかけて、拡張する値を決定します。 例えば、現在のヒープサイズは16MBでGeometric Memory Growth Stepの値が0.2の場合、拡張する値は16 * 0.2 = 3.2MBとなります。

Geometric Memory Growth Cap

Geometric Memory Growth Stepに対する制限です。 例えば、この値が3MBの場合、Geometric Memory Growth Stepの値は3.2MBではなく3MBとなります。

Maximum Memory Size

拡張されたヒープの最大値です。 Memory Growth ModeがLinear及びGeometricの場合でも、この値を超えて拡張されることはありません。

スクリプトから指定する場合

相変わらずスクリプトリファレンスには記載がありませんが、各項目と変数名の対比は下記の通りです。

項目 変数名
Initial Memory Size PlayerSettings.WebGL.initialMemorySize
Maximum Memory Size PlayerSettings.WebGL.maximumMemorySize
Memory Growth Mode PlayerSettings.WebGL.memoryGrowthMode
Linear Memory Growth Step PlayerSettings.WebGL.linearMemoryGrowthStep
Geometric Memory Growth Step PlayerSettings.WebGL.geometricMemoryGrowthStep
Geometric Memory Growth Cap PlayerSettings.WebGL.memoryGeometricGrowthCap

以上です。

WebGL向けにビルドしたコンテンツをローカルPC上で実行してみよう!

概要

WebGLプラットフォーム向けにビルド済みのコンテンツを実行する(index.htmlをダブルクリックする)と以下のエラーが表示され、実行できません。

Failed to download file XXX.data.gz. Loading web pages via a file:// URL without a web server is not supported by this browser. Please use a local development web server to host Unity content, or use the Unity Build and Run option.

Build And Runからの実行ではなく、既にビルド済みのWebGLコンテンツをローカルPCで実行確認したい場合の手順を確認します。

結論

SimpleWebServerを使用します。

SimpleWebServerはUnityEditorの下記の場所に内包されています。 Windowsの場合 {UnityEditorがインストールされているフォルダ}\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools

手順としては下記の通りです。

  1. SimpleWebServerでビルド済みコンテンツを実行する
  2. Webブラウザーで上記コンテンツを開く

例 c:\buildにWebGL向けコンテンツがある(index.htmlが存在する)場合の例です。

事前にSimpleWebServerへのパスを通しておく必要があります。

  1. コマンドプロンプトを起動し次のコマンドを実行する SimpleWebServer.exe c:\build 8000
  2. Webブラウザーを開き次のURLを開く localhost:8000

以上です。

Unity2019以降でWebGLのMemorySizeを指定してみよう!

概要

Unity2018まではPlayerSettingsからMemorySizeを設定出来たのですが、Unity2019以降では項目が無くなりました。 そこでUnity2019以降でMemorySizeを指定するにはどうしたら良いのかというお話しです。

r

なお、Unity2022以降に関しては設定方法が大きく変わっていますので以下をご確認下さい。

unityletsgo.hatenablog.com

結論

PlayerSettings.WebGL.memorySizePlayerSettings.WebGL.emscriptenArgsから設定を行います。

PlayerSettings.WebGL.memorySize

WebGLのメモリの初期値をMB単位で設定します。16の倍数で設定する必要がありますが、16の倍数以外を設定した場合、内部的に16の倍数へ切り上げます。初期値は16MBです。

// MemorySizeの初期値を32MBに設定する
PlayerSettings.WebGL.memorySize = 32;

PlayerSettings.WebGL.emscriptenArgs

初期値では、メモリが不足した場合、メモリをあらたに確保するような設定になっています。 つまり、Memoryの初期値は16MBですが、不足すると16MB->32MBと動的にメモリサイズを増やしていきます。 この動的なメモリの拡張を制限したい場合、PlayerSettings.WebGL.emscriptenArgsから設定を行います。

ALLOW_MEMORY_GROWTH

この値でメモリの拡張を行うかを設定します。

// 動的な拡張を行わない
PlayerSettings.WebGL.emscriptenArgs ="-s ALLOW_MEMORY_GROWTH=0";

// 動的な拡張を行う
PlayerSettings.WebGL.emscriptenArgs ="-s ALLOW_MEMORY_GROWTH=1";

初期値は動的な拡張を行うとなっています。

WASM_MEM_MAX

メモリ拡張の最大値を設定する為に使用します。

// メモリ最大値を512MBに設定する
PlayerSettings.WebGL.emscriptenArgs = "-s WASM_MEM_MAX=512MB";

最大サイズは2032とUnityBlogには記載されています。

まとめ

はじめから、メモリを512MB確保してメモリ拡張を行わない場合

PlayerSettings.WebGL.memorySize = 512;
PlayerSettings.WebGL.emscriptenArgs ="-s ALLOW_MEMORY_GROWTH=0";

初期値は32MBで、最大512MBまで拡張する

PlayerSettings.WebGL.memorySize = 32;
PlayerSettings.WebGL.emscriptenArgs ="-s ALLOW_MEMORY_GROWTH=1";
PlayerSettings.WebGL.emscriptenArgs += "-s WASM_MEM_MAX=512MB";