概要
「FixedUpdate()が実行されないフレームがあるのですが、何故ですか?」 この質問が定期的に発生するので、この問題について解説します。
結論
フレームレートとTime.fixedDeltaTimeが一致していない為に発生します。
解説
UnityはPlayerLoopの先頭で下記の処理を行います。
- 最後のフレームからの経過時間をdeltaTimeとする
- deltaTimeがTime.maximumeDeltaTimeより大きい場合、deltaTimeの値をTime.maximumDeltaTimeとする。
- Time.timeにdeltaTimeを加算する
- Time.fixedTime <= Time.timeの場合、Time.fixedTimeへTime.fixedDeltaTimeを加算しFixedUpdate()を実行し、再び4を繰り返す
- 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の値をフレームレートに合わせることによってフレームとのずれを減らすことが可能