[Unity]タッチ情報のdeltaPositionを使って動かしたオブジェクトの移動量が合わなかった場合の対応


はじめに

Unityのタッチ操作で画面上のオブジェクトなどを動かす場合、オブジェクトの動かし方としては大きく

  • タッチの位置を使ってオブジェクトの位置を変える
  • タッチの移動量を使ってオブジェクトを移動させる

があるかと思います。
後者の場合、Touch.deltaPositionを取得して毎フレームの移動量を決めたりすると思いますが、最初思ったように動かなかったので備忘録です。

結論

  • タッチ情報の更新頻度と毎フレーム処理(Update())のタイミングが異なると移動量がずれる
  • Touch.deltaPositionTouch.deltaTimeで割って速度にして使う

詳細

起きたこと

以下のようなスクリプトを作成し、アタッチしたオブジェクトがタッチ移動に合わせて動くようにしました。

移動量がずれるサンプル
// Update関数
private void Update()
{
    OnTouchOperation();
}

// タッチ移動量に応じてオブジェクトを移動させる処理
private void OnTouchOperation()
{
    // 移動用タッチの情報取得(とりあえずシングルタッチのみ対応)
    if (0 < Input.touchCount)
    {
        Touch touchPos = Input.GetTouch(0);

        // 移動していたら移動量を取得してオブジェクトを移動
        if (TouchPhase.Moved == touchPos.phase)
        {
            this.transform.Translate(touchPos.deltaPosition.x, touchPos.deltaPosition.y, 0.0f);

            // これでもよい
            // this.transform.position = (Vector2)this.transform.position + touchPos.deltaPosition;
        }
    }

    return;
}

※余談:上記コードで各種thisは省略できますが、個人的には明示的に書いておきたい人です

結果:
実際にタッチで移動した距離よりも、オブジェクト(ポインタ)が移動した距離の方が長くなりました。

原因

タッチ情報が取得されるタイミングとUpdate関数のタイミングが違い、Update関数の方が高頻度でTouch.deltaPositionを取得してオブジェクトを動かしていたためです。
逆にUpdate関数の方が頻度が低い場合は、タッチの移動量よりもオブジェクトの移動量の方が少なくなっていたはずです。
大した内容ではないですが一応図解を…

対策

タッチ情報の取得タイミングはTouch.deltaTimeとして取得できるので
タッチ情報の移動量を扱う際はTouch.deltaPositionTouch.deltaTimeで割ることで、移動速度を計算します。
それにUpdate()Time.deltaTimeを乗算することで、そのフレームとしての移動距離が出せます。
(少しは誤差あるかもしれませんが…)

移動量がずれないサンプル
// Update関数
private void Update()
{
    OnTouchOperation();
}

// タッチ移動量に応じてオブジェクトを移動させる処理
private void OnTouchOperation()
{
    // 移動用タッチの情報取得(とりあえずシングルタッチのみ対応)
    if (0 < Input.touchCount)
    {
        Touch touchPos = Input.GetTouch(0);

        // 移動していたら移動量を取得してオブジェクトを移動
        if (TouchPhase.Moved == touchPos.phase)
        {
            Vector2 moveDist = (touchPos.deltaPosition / touchPos.deltaTime) * Time.deltaTime;
            this.transform.Translate(moveDist.x, moveDist.y, 0.0f);

            // これでもよい
            // this.transform.position = (Vector2)this.transform.position + moveDist;
        }
    }

    return;
}

最後に

今回のような手間を加えなくとも、オブジェクト移動量などは何らかの係数をかけてチューニングすれば解決することも多いと思います。
が、タッチ情報取得やUpdate()のフレームレートは環境やその時の負荷で変わることもあると思うので、思わぬ動作に繋がる可能性があると考えています。
気になる動作はできる限り原因を把握して手を打っておきたいものです。

以上です。