GetKeyDownとかGetButtonUpとかがtrueになるタイミングの遷移について https://t.co/TmLfYhMJEA #Qiita
— su10@ハイパーカジュアルゲーム開発 (@su10_dev) January 1, 2021
これもっと具体的にtrueになるタイミングって1フレーム内のどのタイミングなんだろうか?🤔
はじめに
Input.GetMouseButtonDown()
とかを IObservable
にしたいなと思って
の記事を読んで
-
Observable.EveryUpdate()
のタイミングで値がpushされるのがベストかはわからない - できれば使う側が値のpushタイミングを選べたほうが良さそう
-
Input.GetMouseButtonDown()
みたいに好きなときにフラグを確認したい -
IReadOnlyReactiveProperty<bool>
を公開&BatchFrame()
で使う側がpushタイミングを選べればいいのでは?
と思ったのですが、 ReactiveProperty
のフラグを更新するベストなタイミングが1フレームの中のどこなのかわからなかったので調べました。
確認環境
- macOS Catalina(10.15.6)
- Unity2019.4.16
にて確認。
Input.GetMouseButtonDown()
の値が更新されるタイミング
以下のスクリプトを使って同一のフレーム内での値の変化を観察しました。 UniTask.Yield()
を使うとUnityのプレイヤーループの中に独自の実行タイミングを挟み込めるのでそれを使ってます。
また、UniTaskのREADMEによると
await UniTask.WaitForEndOfFrame() is not equilavelnt to coroutine's yield return new WaitForEndOfFrame(). Coroutine's WaitForEndOfFrame seems to run after the PlayerLoop is done. Some methods that require coroutine's end of frame(ScreenCapture.CaptureScreenshotAsTexture, CommandBuffer, etc) does not work correctly when replace to async/await. In that case, use a coroutine.
とのことなのでコルーチンの EndOfFrame
のタイミングでの値も合わせて確認しています。
using System.Collections;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
public class InputTest : MonoBehaviour
{
private bool flag => Input.GetMouseButtonDown(0);
IEnumerator Start()
{
this.MainLoopTask(this.GetCancellationTokenOnDestroy()).Forget();
while (true)
{
yield return new WaitForEndOfFrame();
Debug.Log($"EndOfFrame: {flag}");
}
}
private async UniTask MainLoopTask(CancellationToken ct)
{
while (ct.IsCancellationRequested == false)
{
await UniTask.Yield(PlayerLoopTiming.Initialization, ct);
Debug.LogWarning($"*** frame: {Time.frameCount} ***");
Debug.Log($"Initialization: {flag}");
await UniTask.Yield(PlayerLoopTiming.LastInitialization, ct);
Debug.Log($"LastInitialization: {flag}");
await UniTask.Yield(PlayerLoopTiming.EarlyUpdate, ct);
Debug.Log($"EarlyUpdate: {flag}");
await UniTask.Yield(PlayerLoopTiming.LastEarlyUpdate, ct);
Debug.Log($"LastEarlyUpdate: {flag}");
await UniTask.Yield(PlayerLoopTiming.PreUpdate, ct);
Debug.Log($"PreUpdate: {flag}");
await UniTask.Yield(PlayerLoopTiming.LastPreUpdate, ct);
Debug.Log($"LastPreUpdate: {flag}");
await UniTask.Yield(PlayerLoopTiming.Update, ct);
Debug.Log($"Update: {flag}");
await UniTask.Yield(PlayerLoopTiming.LastUpdate, ct);
Debug.Log($"LastUpdate: {flag}");
await UniTask.Yield(PlayerLoopTiming.PreLateUpdate, ct);
Debug.Log($"PreLateUpdate: {flag}");
await UniTask.Yield(PlayerLoopTiming.LastPreLateUpdate, ct);
Debug.Log($"LastPreLateUpdate: {flag}");
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate, ct);
Debug.Log($"PostLateUpdate: {flag}");
await UniTask.Yield(PlayerLoopTiming.LastPostLateUpdate, ct);
Debug.Log($"LastPostLateUpdate: {flag}");
}
}
}
結果
...
LastPreLateUpdate: False
PostLateUpdate: False
LastPostLateUpdate: False
EndOfFrame: False
*** frame: 28 ***
Initialization: True
LastInitialization: True
EarlyUpdate: True
LastEarlyUpdate: True
PreUpdate: True
LastPreUpdate: True
Update: True
LastUpdate: True
PreLateUpdate: True
LastPreLateUpdate: True
PostLateUpdate: True
LastPostLateUpdate: False
EndOfFrame: False
*** frame: 29 ***
Initialization: False
LastInitialization: False
EarlyUpdate: False
...
何回か適当にクリックして他のタイミングのログも見ましたが、 true/false
の切り替わるタイミングはどれも同じで PostLateUpdate
と LastPostLateUpdate
の間でした。
Input.GetMouseButton()
の値が更新されるタイミング
せっかくなので「押しっぱなしの間 true
」になる方の値も確認してみます。 flag
プロパティをちょっとだけいじりました。
..
public class InputTest : MonoBehaviour
{
private bool flag => Input.GetMouseButton(0); // Downを消した
..
結果
..
LastPostLateUpdate: False
EndOfFrame: False
*** frame: 65 ***
Initialization: True
LastInitialization: True
EarlyUpdate: True
LastEarlyUpdate: True
PreUpdate: True
LastPreUpdate: True
Update: True
LastUpdate: True
PreLateUpdate: True
LastPreLateUpdate: True
PostLateUpdate: True
LastPostLateUpdate: True
EndOfFrame: True
*** frame: 66 ***
Initialization: True
LastInitialization: True
EarlyUpdate: True
LastEarlyUpdate: True
PreUpdate: True
LastPreUpdate: True
Update: True
LastUpdate: True
PreLateUpdate: True
LastPreLateUpdate: True
PostLateUpdate: True
LastPostLateUpdate: True
EndOfFrame: True
*** frame: 67 ***
..
LastPostLateUpdate: True
EndOfFrame: True
*** frame: 101 ***
Initialization: True
LastInitialization: True
EarlyUpdate: True
LastEarlyUpdate: True
PreUpdate: True
LastPreUpdate: True
Update: True
LastUpdate: True
PreLateUpdate: True
LastPreLateUpdate: True
PostLateUpdate: True
LastPostLateUpdate: True
EndOfFrame: True
*** frame: 102 ***
Initialization: False
LastInitialization: False
EarlyUpdate: False
..
今度はきれいに「フレームの最初から最後まで true/false
」で揃いました。
Unityのプレイヤーループで何が起きているのか
↑にリストがあって Input
とついてるのがいくつかありますが、後ろの方の
- InputEndFrame
- ResetInputAxis
あたりでフラグを更新してそうです。これらのイベントを無効化すればさらに詳細な挙動がわかりそうですが、面倒なのでここまでにしておきます。
結論
Input.GetMouseButtonDown()
と Input.GetMouseButton()
でなぜか値の更新タイミングが異なるのが気になりますが、「1フレームの最初に値が決まって最後まで値は変わらない」とみなしてよさそうでした。
参考
-
GetKeyDownとかGetButtonUpとかがtrueになるタイミングの遷移について
- もうちょっと大雑把なタイミングでの値の変化の話