初心者のウチに何か勘違いしたり、よくわからないまま使ってしまうのが、UpdateとFixedUpdateとLateUpdateです。ではこれらの違いはなんなのかというのを解説していきたいと思います。
<フレームについて>
Updateは毎フレーム呼ばれる処理ですが、まずはフレームって何なの?という話をします。
ゲームにおけるフレームというのは、計算して画面に描画する一連の流れの事を言います。
様々な計算を行う
↓
様々な計算を元に画像を作り出す
↓
画面に描画する
この一連の流れを1フレームと言います。
よく言うFPSと言うのは、フレームパーセカンドの略なので、1秒間にこの一連の処理を何回できるかと言う意味になります。
30FPSなら1秒間の間に↑の一連の流れを30回行なっている事になります。
では30FPSは1/ 30 = 0.0333333….. だから1フレーム0.0333333….秒なのかーと思ってしまいます。
これは、合ってはいるんですが、ちょっと勘違いしている可能性が高い考え方になります。
おそらくなんとなーく無意識のうちに平均化して考えている方が多いのではないでしょうか?
例えば1フレーム0.0333333….秒で考えた時
様々な計算を行う(0.0111111…. 秒)
↓
様々な計算を元に画像を作り出す(0.0111111…. 秒)
↓
画面に描画する(0.0111111…. 秒)
みたいな。↑のように無意識のウチに平均化して考えていらっしゃる方もいるのではないでしょうか。さらにずっと一定なイメージがある方もいらっしゃると思います。一定でないとFPSが乱れて画面がカクカクになりますし。
しかし、実際はどうなるかと言うと
様々な計算を行う(0.0△△△…. 秒(何秒か不明、毎回変わる))
↓
様々な計算を元に画像を作り出す(0.0□□□…. 秒(何秒か不明、毎回変わる))
↓
フレーム開始から0.0333333….秒になるように待つ
↓
画面に描画する(全部合わせて0.0333333….秒)
と、こんな感じです。
30FPSの場合、0.0333333….秒になるように待つ事によって一定を保っているわけですね。
ちなみに様々な計算や、様々な計算を元に画像を作り出す作業が間に合わなくて、0.0333333….秒を越えてしまった場合、処理落ちが発生します。
このFPSは基準みたいなものがありまして、60FPS,30FPS,15FPS,0FPSを基準にしています。ディスプレイと同期を取るためにこのような基準にしているみたいです。
FPSが安定しないとガクガクになって見えてしまうので基準を設けてそれに合わせているわけですね。
<各種メソッドの基本>
とりあえず、いったん整理するため、各種Updateの種類について簡単にまとめてみました。
Updateメソッド
このメソッドは1フレームに1回呼ばれます。
毎フレームごとにUnityから呼ばれます。
LateUpdateメソッド
このメソッドは1フレームに1回呼ばれます。
毎フレームごとにUnityから呼ばれますが、様々な計算が終わった後に呼ばれます。
FixedUpdateメソッド
呼ばれた時が一定間隔であるものとして呼ばれるメソッドです。物理演算の前に呼ばれます。
各種メソッドの違い
これらのメソッドをよくみてみると一つ一つ役割が違うことがわかります。
これだけみて、何だかわかった気にはなりますが、何だかつっかえる物があるひともいるのではないでしょうか。
それについて解説していきます。
<UpdateとLateUpdateの違い>
さて、Updateは毎フレーム呼ばれる処理でした。つまりフレームの一連の流れの中で毎回呼ばれる事になります。
ちなみにLateUpdateというのも毎フレーム呼ばれる処理です。これをフレームの中に入れると。
〜〜様々な計算が行われる〜〜〜
>Updateが呼ばれる
〜〜様々な計算が行われる〜〜〜
>最後にLateUpdateが呼ばれる
↓
様々な計算を元に画像を作り出す
↓
フレーム開始から○○秒(FPSに合わせる)になるように待つ
↓
画面に描画する
となります。
このように、Updateは様々な計算の途中に入りますが、LateUpdateは計算の最後に呼ばれます。
UpdateとLateUpdateは1フレームにつき1回呼ばれるのでわかりやすくていいです。
何故LateUpdateが存在するのか
では何故、UpdateとLateUpdateの2種類が存在するのでしょうか?
御察しの通りだと思いますが、Updateで計算した様々な結果を受けてLateUpdateで最終調整ができるからですね。
Unityでのスクリプトの実行順というのはメソッドが同じ種類だと複数存在する時ランダムになります。詳しくは↓の記事で解説しているので参考にしてみてください。
同じ種類のメソッドでの動作順番がわからないから、全部終わってから最終調整できるというわけです。
また、Update→LateUpdateの間にまだまだ様々な処理が挟まっているので、それらが終わってから処理をしたいときに使用すると便利です。
<UpdateとFixedUpdateの違い>
FixedUpdateは文章で解説するのが正直難しいので、↑の動画もぜひご活用して頂ければと思います。
さて、いろんなサイトや解説をみているとFixedUpdateは一定間隔で呼ばれて、Updateとは別物だよとよく言われています。
そして、FixedUpdateには物理演算用の処理を書くとも言われます。
果たして本当にそうでしょうか?ちょっと詳しくみてみましょう。
FixedUpdateの呼ばれ方
そもそもFixedUpdateというのは何なのでしょう?
何だかよくわからないけどわかった気になってしまうのが、「一定間隔で呼ばれる」←これですよね。
Updateとは別の時間周期で一定間隔で呼ばれるみたいです。
が、実は一定間隔で呼ばれるというのは一定秒数で呼ばれるわけではありません。
絶賛勘違いポイントです。この勘違いをしている方は多いのではないでしょうか。
↓でFixedUpdateについて検証していますので、もしよかったらみて見てください。
さて、FixedUpdateは何なのかというのを簡潔に言うと、一定間隔であるかのように見えるようにする処理の中の一部です。詳しくは↑の記事を見ていただくとして、まとめると
>FixedUpdateが呼ばれる
>物理演算が行われる
↑の処理が一定間隔で呼ばれているかのようにn回ループする(nが0の時もある)
↓ループが終わったら下に進む
>Updateが呼ばれる
〜〜様々な計算が行われる〜〜〜
>最後にLateUpdateが呼ばれる
様々な計算を行う
↓
様々な計算を元に画像を作り出す
↓
フレーム開始から○○秒(FPSに合わせる)になるように待つ
↓
画面に描画する
となります。
この「一定間隔」というのは、ProjectSettings>Time>Fixed Timestepで定義されています。
FixedUpdateが呼ばれるたびにこのFixed Timestep分ゲーム時間を進める(現実の時間とは全く別物)というものになります。
FPSを考える時、現実秒数が関係しましたが、FixedUpdateは実は独自の時間で動いています。
ちょっと勘違いしやすいのがこの独自時間はなるべく現実時間と合うようにしているという点です。なのでさも一緒のように動きますが、違うものです。
ではこの部分についてちょっと詳しくみてみましょう。
処理がFixed Timestepより速い場合
もし、60FPSだった場合、1フレーム0.016666…秒なのでFixed Timestepの0.02秒より早いので、
>FixedUpdateが呼ばれる
>物理演算が行われる
この処理が1フレームの間に行われないタイミングがあります。
1フレームの処理速度が物理演算の処理速度より早い為です。nが0の時もあると↑で書いたのはこの為です。
よって1フレーム内にFixedUpdateが0回呼ばれる場合が存在します。
処理がFixed Timestepより遅い場合
もし、30FPSだった場合、1フレーム0.0333333….秒なので、Fixed Timestepの0.02より遅くなります。その為、
>FixedUpdateが呼ばれる
>物理演算が行われる
この処理が1フレームの間に2回行われるタイミングがあります。1フレームの処理速度が物理演算の処理速度より遅い為です。
30FPSの処理の例を時系列で書いてみると
Fixed Timestepの0.02で、30FPSで安定している場合
現実時間 | 現在のフレーム数 | 処理 |
---|---|---|
0秒 | 0フレーム | ー |
0秒 | 0フレーム | FixedUpdate |
?秒 | 0フレーム | Update、LateUpdate |
〜待ち時間〜 | 0フレーム | ー |
0.0333333….秒 | 1フレーム | 画面に描画 |
0.02秒 | 1フレーム | FixedUpdate |
?秒 | 1フレーム | Update、LateUpdate |
〜待ち時間〜 | 1フレーム | ー |
0.066666….秒 | 2フレーム | 画面に描画 |
0.04秒 | 2フレーム | FixedUpdate |
0.06秒 | 2フレーム | FixedUpdate |
?秒 | 2フレーム | Update、LateUpdate |
という感じに1フレームの間にFixedUpdateが2回呼ばれているタイミングがある事がわかります。
処理がすこぶる重い場合
さて、ここまで見ると、確かに一定秒数で呼ばれています。が、処理が重いとちょっと違う挙動になります。
Fixed Timestepの0.02で、30FPSで処理落ちしている場合、
0 現実時間 | 現在のフレーム数 | 処理 |
---|---|---|
0秒 | 0フレーム | ー |
0秒 | 0フレーム | FixedUpdate |
?秒 | 0フレーム | Update、LateUpdate |
0.0333333….秒 | 0フレーム | まだ処理が終わらない |
?秒 | 0フレーム | まだ時間がかかる |
仮に処理に1秒かかったとします。 | 0フレーム | 処理が終わる(1秒経過) |
〜待ち時間〜 | 0フレーム | ー |
1.033333….秒 | 1フレーム | 画面に描画 |
ー | ー | FixedUpdateが呼ばれるが一定間隔とズレているのでFixedUpdateをできる限り呼び出し無理やり時間を進める |
1.033….??秒 | 1フレーム | FixedUpdate |
1.033….??秒 | 1フレーム | FixedUpdate |
1.033….??秒 | 1フレーム | FixedUpdate |
1.033….??秒 | 1フレーム | FixedUpdate |
1.033….??秒 | 1フレーム | FixedUpdate |
ー | ー | FixedUpdateを0.3333333秒分時間を進めるために何回も呼びます |
1.033….??秒 | 1フレーム | 現実時間では何秒も経ってますが、ゲーム内では0.3333333秒経っている事にします。 |
1.033….??秒 | 1フレーム | Update、LateUpdate |
〜待ち時間〜 | 1フレーム | ー |
1.066666….秒 | 2フレーム | 画面に描画 |
とまぁ、こんな感じで可能な限り帳尻合わせを試みます。無理だったら、どれだけ現実時間が経過していようが、ゲーム内時間は
FixedUpdateが呼ばれた回数 × Fixed Timestep
この秒数経ったものとして扱います。
これはUnityが勝手に作った時間軸なので、実際の現実時間の秒数ではないのです。
この経過する秒数は限界値があって、↓のMaximum Allowed Timestepが限界値になります。
そのため、↑の例では仮に1秒経過しているものとしていますが、FixedUpdateが進めた時間は0.3333333になっています。
何故FixedUpdateが存在するのか
↑のように1フレームの間には処理の重さによって時間的なズレが発生します。
物理的な表現をするに当たって、何かとズレていては困ります。(物理の計算には時間を計算に入れるものが多数存在する為)
例えば 速さ = 道のり ÷ 時間 とかモロに時間を計算に使っています。
その為、フレームに合わせた(描画に合わせた)Updateではなく、現実時間となるべく合うようにしたFixedUpdateが存在するのです。
しかしながら、処理が重いと現実時間と完璧に合わせるのが不可能である為、なるべく現実時間に合わせるものの、一定間隔で呼ばれているかのように扱うものがFixedUpdateと言えます。
一定間隔で呼ばれているかのようにして、ズレを無くしている(ように見える)わけですね。
このように、ゲーム内時間を一定間隔として扱う為、計算で時間を使う物理計算などはFixed Timestep基準になります。
FixedUpdateが呼ばれる時Fixed Timestepが更新され、その後に物理計算が行われるので、Fixed Updateに物理的な処理を書いた方がいいと言われるわけです。
<まとめ>
これらUpdate,LateUpdate,FixedUpdateは同じメインスレッドで処理されています。FixedUpdateだけが特殊で、なるべく現実時間に合わせようと努力しますが、無理な時もあります。
Unityの時間軸はFixedUpdateが呼ばれるタイミングを基準にしているので、現実の時間とズレた時は独自の時間軸になるので注意してください。
物理演算でもそれが時間軸に関係しない処理であればUpdateに書いた方がいい場合もあります。
例えばボタンを押したら瞬間的な力を加える処理だったらUpdateに書いた方がいいです。「瞬間的な力」は最初だけでその後の時間経過に関係しない為です。(加えてボタン入力は1フレームの間に読まれるのでFixeUpdateよりUpdateの方がいいです)
FixedUpdateには物理的な処理を書くといいですが、時間が密接に関わっている時にした方がいいかもしれません。キー入力はフレーム内の処理だからです。