Add pinch controls to statue dressup game
This commit is contained in:
212
Assets/Scripts/Minigames/StatueDressup/UI/PinchGestureHandler.cs
Normal file
212
Assets/Scripts/Minigames/StatueDressup/UI/PinchGestureHandler.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user