Files
AppleHillsProduction/Assets/Scripts/Minigames/StatueDressup/UI/PinchGestureHandler.cs

213 lines
8.2 KiB
C#
Raw Normal View History

using System;
using Core;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.EnhancedTouch;
using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch;
namespace Minigames.StatueDressup.UI
{
/// <summary>
/// Detects and processes pinch gestures for scaling and rotation using Unity's new Input System (EnhancedTouch).
/// Also detects single-tap dismissal and provides keyboard shortcuts for editor testing.
/// </summary>
public class PinchGestureHandler : MonoBehaviour
{
[Header("Gesture Settings")]
[Tooltip("Minimum distance change to register as pinch scale gesture")]
[SerializeField] private float minPinchDistance = 10f;
[Tooltip("Sensitivity multiplier for scale changes")]
[SerializeField] private float scaleSensitivity = 0.01f;
[Tooltip("Sensitivity multiplier for rotation changes")]
[SerializeField] private float rotationSensitivity = 1.0f;
[Header("Editor Testing (Keyboard)")]
[Tooltip("Keyboard rotation speed in degrees per second")]
[SerializeField] private float keyboardRotationSpeed = 90f;
[Tooltip("Keyboard scale speed per second")]
[SerializeField] private float keyboardScaleSpeed = 0.5f;
// Events
public event Action<float> OnPinchScale;
public event Action<float> OnPinchRotate;
public event Action OnDismissTap;
private bool _isActive;
private float _previousDistance;
private float _previousAngle;
private double _touchStartTime;
private bool _wasTouchTracked;
/// <summary>
/// Activate gesture detection
/// </summary>
public void Activate()
{
_isActive = true;
_previousDistance = 0f;
_previousAngle = 0f;
_wasTouchTracked = false;
// Enable enhanced touch support for new Input System
if (!EnhancedTouchSupport.enabled)
{
EnhancedTouchSupport.Enable();
}
Logging.Debug("[PinchGestureHandler] Activated");
}
/// <summary>
/// Deactivate gesture detection
/// </summary>
public void Deactivate()
{
_isActive = false;
Logging.Debug("[PinchGestureHandler] Deactivated");
}
private void Update()
{
if (!_isActive) return;
// Handle touch input (mobile) - using new Input System
if (Touch.activeTouches.Count > 0)
{
HandleTouchInput();
}
// Handle keyboard input (editor testing)
else if (Application.isEditor)
{
HandleKeyboardInput();
}
}
/// <summary>
/// Process touch input for pinch gestures and single-tap dismissal
/// </summary>
private void HandleTouchInput()
{
var activeTouches = Touch.activeTouches;
// Single tap for dismissal
if (activeTouches.Count == 1)
{
Touch touch = activeTouches[0];
// Track touch start time
if (touch.phase == UnityEngine.InputSystem.TouchPhase.Began)
{
_touchStartTime = touch.startTime;
_wasTouchTracked = true;
}
// Detect tap (touch began and ended quickly - within 0.3 seconds)
else if (touch.phase == UnityEngine.InputSystem.TouchPhase.Ended && _wasTouchTracked)
{
double touchDuration = Time.timeAsDouble - _touchStartTime;
if (touchDuration < 0.3)
{
Logging.Debug("[PinchGestureHandler] Single tap detected for dismissal");
OnDismissTap?.Invoke();
}
_wasTouchTracked = false;
}
}
// Two-finger pinch for scale and rotation
else if (activeTouches.Count == 2)
{
Touch touch0 = activeTouches[0];
Touch touch1 = activeTouches[1];
// Calculate current positions and distance
Vector2 currentTouch0 = touch0.screenPosition;
Vector2 currentTouch1 = touch1.screenPosition;
float currentDistance = Vector2.Distance(currentTouch0, currentTouch1);
float currentAngle = Mathf.Atan2(currentTouch1.y - currentTouch0.y, currentTouch1.x - currentTouch0.x) * Mathf.Rad2Deg;
// Initialize on first frame of pinch
if (touch0.phase == UnityEngine.InputSystem.TouchPhase.Began || touch1.phase == UnityEngine.InputSystem.TouchPhase.Began)
{
_previousDistance = currentDistance;
_previousAngle = currentAngle;
Logging.Debug("[PinchGestureHandler] Pinch gesture started");
return;
}
// Process pinch scale (distance change)
if (touch0.phase == UnityEngine.InputSystem.TouchPhase.Moved || touch1.phase == UnityEngine.InputSystem.TouchPhase.Moved)
{
if (_previousDistance > minPinchDistance)
{
float distanceDelta = currentDistance - _previousDistance;
float scaleDelta = distanceDelta * scaleSensitivity;
if (Mathf.Abs(scaleDelta) > 0.001f)
{
OnPinchScale?.Invoke(scaleDelta);
Logging.Debug($"[PinchGestureHandler] Scale delta: {scaleDelta:F3}");
}
}
// Process pinch rotation (angle change)
float angleDelta = Mathf.DeltaAngle(_previousAngle, currentAngle) * rotationSensitivity;
if (Mathf.Abs(angleDelta) > 0.5f)
{
OnPinchRotate?.Invoke(angleDelta);
Logging.Debug($"[PinchGestureHandler] Rotation delta: {angleDelta:F1}°");
}
// Update previous values
_previousDistance = currentDistance;
_previousAngle = currentAngle;
}
}
}
/// <summary>
/// Process keyboard input for editor testing
/// Q/E for rotation, +/- for scale, Escape for dismissal
/// </summary>
private void HandleKeyboardInput()
{
var keyboard = Keyboard.current;
if (keyboard == null) return;
// Rotation with Q/E
if (keyboard.qKey.isPressed)
{
float rotateDelta = -keyboardRotationSpeed * Time.deltaTime;
OnPinchRotate?.Invoke(rotateDelta);
}
else if (keyboard.eKey.isPressed)
{
float rotateDelta = keyboardRotationSpeed * Time.deltaTime;
OnPinchRotate?.Invoke(rotateDelta);
}
// Scale with +/- (equals key is where + is on keyboard)
if (keyboard.equalsKey.isPressed || keyboard.numpadPlusKey.isPressed)
{
float scaleDelta = keyboardScaleSpeed * Time.deltaTime;
OnPinchScale?.Invoke(scaleDelta);
}
else if (keyboard.minusKey.isPressed || keyboard.numpadMinusKey.isPressed)
{
float scaleDelta = -keyboardScaleSpeed * Time.deltaTime;
OnPinchScale?.Invoke(scaleDelta);
}
// Dismissal with Escape or Space
if (keyboard.escapeKey.wasPressedThisFrame || keyboard.spaceKey.wasPressedThisFrame)
{
Logging.Debug("[PinchGestureHandler] Keyboard dismissal detected");
OnDismissTap?.Invoke();
}
}
}
}