2016-07-18 12 views
0

私はUnity3dのAndroidタブレットデバイス用のスクリプトで、ユーザーがカメラを移動するためにドラッグしています。私は彼がパニングしている間、ユーザーの指の下に留まるように、タッチ位置にある "グラウンド"を望んでいます。ここに私のsimplyfied動作するコードは、これまでのところです:今Unity3dの慣性を持つAndroidタッチカメラコントロール

using UnityEngine; 

public class CameraMovement : MonoBehaviour 
{ 
    Plane plane = new Plane(Vector3.forward, Vector3.zero); 
    Vector2 worldStartPoint; 

    void Update() 
    { 
     if (Input.touchCount == 1) 
     { 
      Touch touch = Input.GetTouch(0); 

      if (touch.phase == TouchPhase.Began) 
      { 
       this.worldStartPoint = GetWorldPoint(touch.position); 
      } 

      if (touch.phase == TouchPhase.Moved) 
      { 
       Vector2 worldDelta = GetWorldPoint(touch.position) - worldStartPoint; 
       transform.Translate(-worldDelta.x, -worldDelta.y, 0); 
      } 
     } 
    } 

    Vector2 GetWorldPoint(Vector2 screenPoint) 
    { 
     Ray ray = Camera.main.ScreenPointToRay(screenPoint); 
     float rayDistance; 
     if (plane.Raycast(ray, out rayDistance)) 
      return ray.GetPoint(rayDistance); 

     return Vector2.zero; 
    } 
} 

問題の一部:私は、ユーザーが自分の指を持ち上げた後、カメラが物理オブジェクトのように移動したいと思います。私は現在の速度をドラッグしている間に計算し、それをダンプした/慣性のような効果として現在ドラッグしていない間に適用しようとしています。理論的には私はこれだろう。だから、

Vector2 worldStartPoint; 
Vector3 velocity; 

void Update() 
{ 
    if (Input.touchCount == 1) 
    { 
     Touch touch = Input.GetTouch(0); 

     if (touch.phase == TouchPhase.Began) 
     { 
      this.worldStartPoint = GetWorldPoint(touch.position); 
     } 

     if (touch.phase == TouchPhase.Moved) 
     { 
      Vector2 worldDelta = GetWorldPoint(touch.position) - worldStartPoint; 
      transform.Translate(-worldDelta.x, -worldDelta.y, 0); 

      velocity = worldDelta/Time.deltaTime; 
     } 
    } 
    else 
    { 
     transform.Translate(-velocity * Time.deltaTime); 
     velocity = Vector3.MoveTowards(velocity, Vector3.zero, damping * Time.deltaTime); 
    } 
} 

を動かしながら、私はいつもの速度を計算し、私は入力を停止したら、それは、最後の既知の速度のままにそれを適用し、停止するまでに、そこから減らす必要があります。しかし、最後のベロシティはゼロです。なぜなら、画面全体をドラッグ/スワイプすると、指が実際に短時間停止し、TouchPhase.Movedが終了する前に速度がゼロになっているように見えるからです。

私の解決策/回避策は、最後の数フレーム(おそらく30)の速度の配列を維持し、指を持ち上げると平均速度を計算することです。

Vector3[] velocityBuffer = new Vector3[bufferSize]; 
int nextBufferIndex; 

Vector3 GetAverage() 
{ 
    Vector3 sum = Vector3.zero; 
    for (int i = 0; i < bufferSize; i++) 
     sum += velocityBuffer[i]; 

    return sum/bufferSize; 
} 

これは少し良く機能し、以来、少なからずそれが、少なくともいくつかの速度を報告しますが、合計で、それははるかに良いではありませんし、また非常にハック感じています。タッチの速さによっては、速度がゼロになってダンピングが非常に強くなりすぎることがあります。速度がランダムに大きくなりすぎて、数百のユニットが飛んでしまうことがあります。

私の計算に何か問題はありますか、これを解決する簡単な方法を見習っていますか?誰かが滑らかなカメラドラッグの実用的な解決策を持っていますか?私はいくつかのモバイルゲームを見て、実際には、デルタムービングの数ピクセルの後に、指の位置にカメラをスナップするような、ちょっと不器用な感じがしていました。

答えて

0

私は完璧な解決策を見つけたようには感じませんが、少なくとも今はもっとうまくいくし、より物理的に正しいものもあります。まず、速度の代わりにカメラの位置とdeltaTimeをバッファに保存します。このようにして、正確な時間係数で10フレームごとにローリング平均を計算することができます。

/// <summary> 
/// Stores Vector3 samples such as velocity or position and returns the average. 
/// </summary> 
[Serializable] 
public class Vector3Buffer 
{ 
    public readonly int size; 

    Sample[] sampleData; 
    int nextIndex; 

    public Vector3Buffer(int size) 
    { 
     if (size < minSize) 
     { 
      size = minSize; 
      Debug.LogWarning("Sample count must be at least one. Using default."); 
     } 

     this.size = size; 
     sampleData = new Sample[size]; 
    } 

    public void AddSample(Vector3 position, float deltaTime) 
    { 
     sampleData[nextIndex] = new Sample(position, deltaTime); 
     nextIndex = ++nextIndex % size; 
    } 

    public void Clear() 
    { 
     for (int i = 0; i < size; i++) 
      sampleData[i] = new Sample(); 
    } 

    public Vector3 GetAverageVelocity(Vector3 currentPosition, float currentDeltaTime) 
    { 
     // The recorded sample furthest back in time. 
     Sample previous = sampleData[nextIndex % size]; 
     Vector3 positionDelta = currentPosition - previous.position; 
     float totalTime = currentDeltaTime; 
     for (int i = 0; i < size; i++) 
      totalTime += sampleData[i].deltaTime; 

     return positionDelta/totalTime; 
    } 

    [Serializable] 
    struct Sample 
    { 
     public Vector3 position; 
     public float deltaTime; 

     public Sample(Vector3 position, float deltaTime) 
     { 
      this.position = position; 
      this.deltaTime = deltaTime; 
     } 
    } 

    public const int minSize = 1; 
} 

はまた、私はまた、ドラッグしていない時に更新したい位置を、追跡するが、保持していますので、私は今、緩和され、ゼロ速度値、の多くを記録したこと、気づきました

if (input.phase == TouchPhase.Moved || input.phase == TouchPhase.Stationary) 
{ 
    velocityBuffer.AddSample(transform.position, Time.deltaTime); 
} 

if (input.phase == TouchPhase.Ended || input.phase == TouchPhase.Canceled) 
{ 
    velocity = -velocityBuffer.GetAverageVelocity(transform.position, Time.deltaTime); 
} 

最後に、フレームごとにフィンガワールド位置にカメラを設定する代わりに、少しのLerp/MoveTowards補間を使用してジッタを滑らかにします。鮮明なコントロールと滑らかな外観の間で最高の価値を得るのは難しいですが、私はこれがユーザーの入力に影響を与える方法であると考えています。

もちろん、私はまだ私の現在の実装に関する他のアプローチ、よりよい解決策または意見に興味があるでしょう。

関連する問題