【Unity】ピンチ操作でUIをズームイン/ズームアウトする

初めに

ピンチ操作でUIをズームするコードを紹介している記事が少なく、表現したい動きがなかなか作れなかったので色々試して一番しっくりくる動きをしたコードを紹介します。

参考サイト

次のサイトのUIの拡縮処理を参考(ベース)にさせていただきました。

kohki.hatenablog.jp

スクリプト

参考サイトのスクリプトから、意図しない移動や拡縮を極力削除したスクリプトになります。

using UnityEngine;
using UnityEngine.UI;

public class PinchScalingManager : MonoBehaviour {

    [SerializeField]
    private ScrollRect scrollRect;    //拡大縮小するコンテンツ
    [SerializeField]
    private RectTransform contentRect;  //拡大縮小するコンテンツ

    //コンテンツのRectTransformの参照
    [SerializeField]
    private float scale;    //現在の拡大率

    [System.Serializable]
    struct RangeClass
    {
        public float min, max;
    }

    [SerializeField]
    private RangeClass RangeScale;          //拡大縮小の範囲

    [SerializeField]
    private float TweenSecond;      //収束するまでにかかる時間

    private bool isPinch = false;       //ピンチ中であればtrue
    private Vector3 center;             //現在の中心座標

#if UNITY_EDITOR
#elif UNITY_IOS || UNITY_ANDROID
    private float max_distance = 0;     //ピンチ開始時の指間の距離
#endif


    void Start () {
        center = contentRect.localPosition / scale;

        //状態の初期化
        UpdateScaling ();

        //表示されている画面の中心を拡大率に合わせて調整する
        contentRect.anchoredPosition *= scale;
    }


    void Update () {

#if UNITY_EDITOR
        bool isZoom = Input.GetAxisRaw("Vertical") != 0;
#elif UNITY_IOS || UNITY_ANDROID
        bool isZoom = Input.touchCount == 2;
#endif

        //タッチ中の処理
        if (isPinch)
        {
            //タッチ終了を感知し、終了処理をする
            if (!isZoom)
            {
                isPinch = false;
                return;
            }

#if UNITY_EDITOR
            float newScale = scale + Input.GetAxisRaw("Vertical") * Time.deltaTime;
#elif UNITY_IOS || UNITY_ANDROID
            float distance = Vector2.Distance (Input.touches [0].position, Input.touches [1].position);
            float newScale = distance / max_distance;
#endif

            SetNewScale(newScale);
            UpdateScaling();
            return;
        }
        else
        {
            scrollRect.enabled = true;
        }

        //タッチ開始時を感知し、初期化処理をする
        if (isZoom)
        {
            center = contentRect.localPosition / scale;
            isPinch = true;
            scrollRect.enabled = false;

#if UNITY_EDITOR
#elif UNITY_IOS || UNITY_ANDROID
            float distance = Vector2.Distance(Input.touches[0].position, Input.touches[1].position);
            max_distance = distance / scale;
#endif
        }
    }

    /// <summary>
    /// 新しい拡大率のバリデートと更新をする
    /// </summary>
    private void SetNewScale(float new_scale){
        // min < 新しい拡大率 < max に設定する
        new_scale = Mathf.Min (new_scale, RangeScale.max);
        new_scale = Mathf.Max (new_scale, RangeScale.min);

        scale = new_scale;
    }


    /// <summary>
    /// 設定された拡大率に基づいてオブジェクトの大きさを更新する
    /// </summary>
    private void UpdateScaling(){
        contentRect.localPosition = center * scale;
        contentRect.localScale = new Vector3(scale, scale, 1);
    }
}

Unity

参考サイトの構成から極力使用しないオブジェクトを削除しました。 ScrollViewのContent以下が拡縮対象になります。