using UnityEngine; using Lean.Common; using FSA = UnityEngine.Serialization.FormerlySerializedAsAttribute; namespace Lean.Touch { /// This component allows you to scale the current GameObject relative to the specified camera using the pinch gesture. [HelpURL(LeanTouch.HelpUrlPrefix + "LeanPinchScale")] [AddComponentMenu(LeanTouch.ComponentPathPrefix + "Pinch Scale")] public class LeanPinchScale : MonoBehaviour { /// The method used to find fingers to use with this component. See LeanFingerFilter documentation for more information. public LeanFingerFilter Use = new LeanFingerFilter(true); /// The camera that will be used to calculate the zoom. /// None/null = MainCamera. public Camera Camera { set { _camera = value; } get { return _camera; } } [FSA("Camera")] [SerializeField] private Camera _camera; /// Should the scaling be performed relative to the finger center? public bool Relative { set { relative = value; } get { return relative; } } [FSA("Relative")] [SerializeField] private bool relative; /// The sensitivity of the scaling. /// 1 = Default. /// 2 = Double. public float Sensitivity { set { sensitivity = value; } get { return sensitivity; } } [FSA("Sensitivity")] [SerializeField] private float sensitivity = 1.0f; /// If you want this component to change smoothly over time, then this allows you to control how quick the changes reach their target value. /// -1 = Instantly change. /// 1 = Slowly change. /// 10 = Quickly change. public float Damping { set { damping = value; } get { return damping; } } [FSA("Damping")] [FSA("Dampening")] [SerializeField] private float damping = -1.0f; [SerializeField] private Vector3 remainingScale; /// If you've set Use to ManuallyAddedFingers, then you can call this method to manually add a finger. public void AddFinger(LeanFinger finger) { Use.AddFinger(finger); } /// If you've set Use to ManuallyAddedFingers, then you can call this method to manually remove a finger. public void RemoveFinger(LeanFinger finger) { Use.RemoveFinger(finger); } /// If you've set Use to ManuallyAddedFingers, then you can call this method to manually remove all fingers. public void RemoveAllFingers() { Use.RemoveAllFingers(); } #if UNITY_EDITOR protected virtual void Reset() { Use.UpdateRequiredSelectable(gameObject); } #endif protected virtual void Awake() { Use.UpdateRequiredSelectable(gameObject); } protected virtual void Update() { // Store var oldScale = transform.localPosition; // Get the fingers we want to use var fingers = Use.UpdateAndGetFingers(); // Calculate pinch scale, and make sure it's valid var pinchScale = LeanGesture.GetPinchScale(fingers); if (pinchScale != 1.0f) { pinchScale = Mathf.Pow(pinchScale, sensitivity); // Perform the translation if this is a relative scale if (relative == true) { var pinchScreenCenter = LeanGesture.GetScreenCenter(fingers); if (transform is RectTransform) { TranslateUI(pinchScale, pinchScreenCenter); } else { Translate(pinchScale, pinchScreenCenter); } } transform.localScale *= pinchScale; remainingScale += transform.localPosition - oldScale; } // Get t value var factor = LeanHelper.GetDampenFactor(damping, Time.deltaTime); // Dampen remainingDelta var newRemainingScale = Vector3.Lerp(remainingScale, Vector3.zero, factor); // Shift this transform by the change in delta transform.localPosition = oldScale + remainingScale - newRemainingScale; // Update remainingDelta with the dampened value remainingScale = newRemainingScale; } protected virtual void TranslateUI(float pinchScale, Vector2 pinchScreenCenter) { var camera = _camera; if (camera == null) { var canvas = transform.GetComponentInParent(); if (canvas != null && canvas.renderMode != RenderMode.ScreenSpaceOverlay) { camera = canvas.worldCamera; } } // Screen position of the transform var screenPoint = RectTransformUtility.WorldToScreenPoint(camera, transform.position); // Push the screen position away from the reference point based on the scale screenPoint.x = pinchScreenCenter.x + (screenPoint.x - pinchScreenCenter.x) * pinchScale; screenPoint.y = pinchScreenCenter.y + (screenPoint.y - pinchScreenCenter.y) * pinchScale; // Convert back to world space var worldPoint = default(Vector3); if (RectTransformUtility.ScreenPointToWorldPointInRectangle(transform.parent as RectTransform, screenPoint, camera, out worldPoint) == true) { transform.position = worldPoint; } } protected virtual void Translate(float pinchScale, Vector2 screenCenter) { // Make sure the camera exists var camera = LeanHelper.GetCamera(_camera, gameObject); if (camera != null) { // Screen position of the transform var screenPosition = camera.WorldToScreenPoint(transform.position); // Push the screen position away from the reference point based on the scale screenPosition.x = screenCenter.x + (screenPosition.x - screenCenter.x) * pinchScale; screenPosition.y = screenCenter.y + (screenPosition.y - screenCenter.y) * pinchScale; // Convert back to world space transform.position = camera.ScreenToWorldPoint(screenPosition); } else { Debug.LogError("Failed to find camera. Either tag your cameras MainCamera, or set one in this component.", this); } } } } #if UNITY_EDITOR namespace Lean.Touch.Editor { using TARGET = LeanPinchScale; [UnityEditor.CanEditMultipleObjects] [UnityEditor.CustomEditor(typeof(TARGET), true)] public class LeanPinchScale_Editor : LeanEditor { protected override void OnInspector() { TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts); Draw("Use"); Draw("_camera", "The camera that will be used to calculate the zoom.\n\nNone/null = MainCamera."); Draw("relative", "Should the scaling be performed relative to the finger center?"); Draw("sensitivity", "The sensitivity of the scaling.\n\n1 = Default.\n\n2 = Double."); Draw("damping", "If you want this component to change smoothly over time, then this allows you to control how quick the changes reach their target value.\n\n-1 = Instantly change.\n\n1 = Slowly change.\n\n10 = Quickly change."); } } } #endif