指1本でドラッグ、指2本でピンチイン・ピンチアウトするスクリプトの紹介。
以前にも似たような覚書を書いてます。
こういった数学系のスクリプトって嫌いな人は書きたくもないだろうし、コピペで済ませられれば他に注力できますね。
次のスクリプトをImageなんかに追加する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; public class TapCtrl : MonoBehaviour, IPointerClickHandler, IPointerDownHandler{ bool isPinch =false; float time=0; //時間計測 Vector3 sPos,nPos; //タッチした座標,座標間距離 Quaternion sRot; //タッチしたときの回転 float vMin = 0.2f , vMax = 5.0f, sDist = 0.0f, nDist = 0.0f; //倍率制限,距離変数 static Vector3 initScale; //最初の大きさ static float v = 1.0f; //現在倍率 float V2NmlzCross(Vector2 v1,Vector2 v2){ return v1.normalized.x*v2.normalized.y - v1.normalized.y*v2.normalized.x; } void Update(){ time += Time.deltaTime; if(Input.touchCount == 1) { //ドラッグ if (Input.GetTouch(0).phase == TouchPhase.Began || isPinch){ sPos = Input.mousePosition; isPinch = false; } else { this.transform.position += Input.mousePosition - sPos; sPos = Input.mousePosition; } }else if(Input.touchCount >= 2) { //ピンチイン ピンチアウト isPinch = true; time=99; Touch t1 = Input.GetTouch (0), t2 = Input.GetTouch (1); if (t2.phase == TouchPhase.Began) { initScale = this.transform.localScale; v=1f; sDist = Vector2.Distance (t1.position, t2.position); sRot = this.transform.rotation; sPos = t2.position - t1.position; } else if ((t1.phase == TouchPhase.Moved||t1.phase == TouchPhase.Stationary) && (t2.phase == TouchPhase.Moved||t2.phase == TouchPhase.Stationary) ) { nDist = Vector2.Distance (t1.position, t2.position); v = Mathf.Clamp(v + (nDist - sDist) / Screen.width , vMin , vMax); this.transform.localScale = initScale * v; sDist = nDist; nPos=t2.position-t1.position; this.transform.Rotate( new Vector3(0,0,Vector2.Angle(sPos,nPos) * Mathf.Sign( V2NmlzCross(sPos,nPos) ) ) ); sPos=nPos; } } } public void OnPointerDown( PointerEventData data ){ time=0; } public void OnPointerClick( PointerEventData data ){ if(Input.touchCount<2&&time<0.2f){ /* TAP Event */ } } } |
注意点
Unityエディタでは Input.touchCount が0になるので動作しません。
0.2秒以内にタップし終わるとタップイベントが始まります。
時間を加味しないやり方だとフラグを追加して TouchPhase.Begin で true TouchPhase.Moved で false にすることもできます。
ドラッグは EventSystem を使って書くこともできる。
1 2 3 |
public void OnDrag(PointerEventData eventData){ if(Input.touchCount<2)this.transform.position = Input.mousePosition; } |
この場合、指の移動にかかわらず対象は指の場所に移動する。
複数に適応するなら全部が動かないようにアクティブな GameObject を対象にするように変更が必要です。
Updateでの処理があるので大量のオブジェクトに適応したいならUpdateでの処理は単一のコントローラーに任せて、タップに合わせてターゲットを変えるほうが良いと思います。
所感
回転部分の理解がつらい。ごり押しで無駄な計算をしてる気がする。
移動、拡縮、回転では始まりからの変化を意識しないととんでもない結果になる。
またタッチする指は0→1→0、0→2→0だけでなくて、いろんなケースがあるのが面倒くさい。
追記
このままだと設定ウィンドウを開いて操作をした時にも移動する。
ボタンを押すだけでも Input.GetTouch(0).phase の関係で変な動きになります。
UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject() による判定もできますが、移動対象にマスクをかけていたりすると使えません。
そこで今回のコードに以下を付け足します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
... [SerializeField] GameObject window1,window2; bool isReady = true; IEnumerator ToReady(){ yield return new WaitForEndOfFrame(); isReady = true; } void Update(){ if(!window1.activeSelf&&!window2.activeSelf){ StartCoroutine(ToReady()); }else{ isReady = false; } if(isReady){ //移動処理 } } |
これで特定の window が開いている間は操作できなくなります。
フレーム終了まで待つ処理を入れているのは、閉じるボタン対策です。
これがないと閉じた瞬間のタップが検知されてphaseの関係で誤動作します。