using UnityEngine; /// /// Attach to each selectable layer child. /// Supports: /// • Single touch / mouse drag → rotate /// • Two-finger pinch → scale (zoom in/out) /// • Two-finger drag → pan (move) /// • Mouse scroll wheel → scale (zoom in/out) /// • Animator on this object → animation plays and can be scaled/moved freely /// public class LayerInteract : MonoBehaviour { // --------------------------------------------------------------- // Inspector // --------------------------------------------------------------- [Header("Layer Info")] public string layerTitle = "Nama Layer"; [TextArea] public string layerDescription = "Deskripsi layer ini..."; public int narasiClipIndex = -1; // index AudioClip di LayerInfoPanel.narasiClips [Header("Interaction Settings")] [Tooltip("Rotation speed multiplier for touch/mouse drag.")] public float rotateSpeed = 0.3f; [Tooltip("Pinch zoom sensitivity.")] public float pinchSensitivity = 0.005f; [Tooltip("Pan (two-finger move) sensitivity.")] public float panSensitivity = 0.01f; [Tooltip("Mouse scroll wheel sensitivity.")] public float scrollSensitivity = 0.3f; [Tooltip("Min / max scale multiplier relative to original scale.")] public Vector2 scaleRange = new Vector2(0.4f, 3.0f); // --------------------------------------------------------------- // Private state // --------------------------------------------------------------- private LayerSelector selector; private bool isSelected = false; private bool justSelected = false; // skip first-frame drag after tap // Scale tracking private Vector3 originalScale; // scale when SetSelected(true) is called private float scaleMultiplier = 1f; // Pinch state private float lastPinchDist = 0f; private bool pinchInitialized = false; // Animator support private Animator animator; // --------------------------------------------------------------- // Unity lifecycle // --------------------------------------------------------------- void Awake() { // Cache Animator if present (e.g., tectonic plate animation) animator = GetComponent(); // Ensure a collider exists for raycasting if (GetComponent() == null) gameObject.AddComponent(); } void Start() { selector = GetComponentInParent(); if (selector == null) Debug.LogError($"[LayerInteract] {gameObject.name}: LayerSelector not found in parent!"); } void Update() { if (!isSelected) return; HandleTouchInput(); HandleMouseInput(); justSelected = false; } // --------------------------------------------------------------- // Input handling // --------------------------------------------------------------- void HandleTouchInput() { int touchCount = Input.touchCount; if (touchCount == 1) { pinchInitialized = false; // reset pinch when back to one finger Touch t = Input.GetTouch(0); if (t.phase == TouchPhase.Moved) RotateObject(t.deltaPosition.x, t.deltaPosition.y); } else if (touchCount >= 2) { Touch t1 = Input.GetTouch(0); Touch t2 = Input.GetTouch(1); // ---- Pinch zoom ---- float currentDist = Vector2.Distance(t1.position, t2.position); if (!pinchInitialized || t1.phase == TouchPhase.Began || t2.phase == TouchPhase.Began) { lastPinchDist = currentDist; pinchInitialized = true; } else { float delta = currentDist - lastPinchDist; ApplyScaleDelta(delta * pinchSensitivity); lastPinchDist = currentDist; } if (t1.phase == TouchPhase.Moved || t2.phase == TouchPhase.Moved) { Vector2 avgDelta = (t1.deltaPosition + t2.deltaPosition) * 0.5f; if (Vector2.Dot(t1.deltaPosition.normalized, t2.deltaPosition.normalized) > 0.5f) { Vector3 worldDelta = Camera.main.transform.TransformDirection( new Vector3(avgDelta.x, avgDelta.y, 0f) * panSensitivity ); transform.position += worldDelta; } } } } void HandleMouseInput() { // Rotate with left mouse button drag (skip the frame of selection) if (Input.GetMouseButton(0) && !justSelected) { float dx = Input.GetAxis("Mouse X"); float dy = Input.GetAxis("Mouse Y"); RotateObject(dx * (1f / rotateSpeed), dy * (1f / rotateSpeed)); // Note: Mouse X/Y are already in a "delta" unit, so we scale them separately } // Scroll wheel zoom float scroll = Input.GetAxis("Mouse ScrollWheel"); if (Mathf.Abs(scroll) > 0.001f) ApplyScaleDelta(scroll * scrollSensitivity); } // --------------------------------------------------------------- // Helpers // --------------------------------------------------------------- /// Horizontal delta in screen pixels (or axis value). /// Vertical delta in screen pixels (or axis value). void RotateObject(float deltaX, float deltaY) { float rotY = -deltaX * rotateSpeed; float rotX = deltaY * rotateSpeed; transform.Rotate(Camera.main.transform.up, rotY, Space.World); transform.Rotate(Camera.main.transform.right, rotX, Space.World); } void ApplyScaleDelta(float delta) { scaleMultiplier = Mathf.Clamp(scaleMultiplier + delta, scaleRange.x, scaleRange.y); transform.localScale = originalScale * scaleMultiplier; } // --------------------------------------------------------------- // Selection API // --------------------------------------------------------------- void OnMouseDown() { TapSelect(); } public void TapSelect() { if (selector == null) { selector = GetComponentInParent(); if (selector == null) return; } // Only select if nothing is currently selected if (selector.selectedLayer == null) { selector.SelectLayer(this.transform); justSelected = true; } } public void SetSelected(bool val) { isSelected = val; if (val) { originalScale = transform.localScale; if (originalScale == Vector3.zero) originalScale = Vector3.one; scaleMultiplier = 1f; pinchInitialized = false; // Resume animation if present if (animator != null) animator.enabled = true; } else { scaleMultiplier = 1f; pinchInitialized = false; } } }