213 lines
7.1 KiB
C#
213 lines
7.1 KiB
C#
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
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<Animator>();
|
|
// Ensure a collider exists for raycasting
|
|
if (GetComponent<Collider>() == null)
|
|
gameObject.AddComponent<SphereCollider>();
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
selector = GetComponentInParent<LayerSelector>();
|
|
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
|
|
// ---------------------------------------------------------------
|
|
|
|
/// <param name="deltaX">Horizontal delta in screen pixels (or axis value).</param>
|
|
/// <param name="deltaY">Vertical delta in screen pixels (or axis value).</param>
|
|
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<LayerSelector>();
|
|
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;
|
|
|
|
}
|
|
}
|
|
} |