0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity】その古いUniTask、タイミングずれてますよ ~キー入力が取れなくて困った話~

Posted at

Unity2019から正式リリースされた新しいInputSystemには、ボタンを押した(離した)フレームのみTrueになるWasPressedThisFrame()とWasReleasedThisFrame()というメソッドがあります。

このメソッドの値をUniTask.Yield()を使ってUpdateのタイミングでポーリングしようとしたのですが、WasReleasedThisFrame()が常にfalseを返し、入力を検知できない現象が発生しました
原因は使用しているUniTaskとUnityのバージョンが合っていなかったという単純なものだったのですが、特定に至るまでが大変だったので1、将来同じ問題に直面する方のために情報を残しておきます。

環境

  • Unity 6 (6000.0.40f1)
  • InputSystem 1.13.1
    • Update Mode:Process Events In Dynamic Update
    • PlayerInput未使用
  • UniTask 2.0.24

解決方法

UniTaskをUnity 6をサポートしているバージョンにアップデートすることで解決しました。
筆者の場合は2.0.24から2.5.10(2025年5月6日時点の最新)にアップデートしてWasPressedThisFrame()から正しく値が取れるようになりました。

原因

古いUniTaskをUnity2020.2以降で使用した場合に発生する不具合でした。
Unity2020.2からPlayerLoopのイベントの種類(TimeUpdate)が増えた影響でUniTaskのイベント実行タイミングがずれ、PlayerLoopTiming.UpdateならPreUpdateのタイミングでYield()を抜けてしまい、まだ値のセットされていないWasPressedThisFrame()にアクセスしてしまっていたようです(InputSystemはPreUpdateで値をセットします)。

この不具合はUniTask 2.1.0で修正されています。当時アナウンスされていたのですが見落としていました。

おまけ:問題が発生したコード

UniTaskでInputActionをポーリングして、WasPressedThisFrame()がtrueになったらイベントを発行するコードです。
バージョン2.1.0未満のUniTaskをUnity 2020.2以降で使用するとPreUpdateのタイミングでYield()を抜けてWasPressedThisFrame()にアクセスしてしまい、必ずfalseが返ります。

InputActionWatcher.cs
using Cysharp.Threading.Tasks;
using System.Threading;
using UnityEngine;

class InputActionWatcher : MonoBehaviour
{
    private void Start()
    {
        RunUpdater(this.GetCancellationTokenOnDestroy()).Forget();
    }

    async UniTaskVoid RunUpdater(CancellationToken cancel)
    {
        // InputActionAssetが自動生成したクラスのインスタンスを作成
        // 実際の運用ではstaticで定義するなど他と共有できるようにした方が良い
        var input = new MyInputActions();
        while (!cancel.IsCancellationRequested)
        {
            // 攻撃ボタンがこのフレームに押されたかチェック
            if(input.Battle.Attack.WasPressedThisFrame())
            {
                // ボタンが押されたことを通知
                CallbackOnPressed();
            }
            await UniTask.Yield(PlayerLoopTiming.Update, cancel);
        }
    }

    private void CallbackOnPressed() 
    {
        Debug.Log("Attack button pressed");        
    }
}

  1. 最初はPlaerLoopのイベント実行順がUnityの仕様で順不同になることが原因と予想していたため、かなり遠回りをしました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?