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;
}
}
}