Files
ichni_Official/Assets/Scripts/Manager/RhythmInputManager.cs

210 lines
7.2 KiB
C#
Raw Normal View History

2025-07-21 05:42:20 -04:00
using UnityEngine;
using UnityEngine.InputSystem;
using System;
using System.Collections.Generic;
using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch;
using TouchPhase = UnityEngine.InputSystem.TouchPhase;
/// <summary>
/// 为节奏游戏设计的输入管理器,处理多点触控并分发三种主要事件。
/// 【重要】此版本内置了编辑器内的鼠标模拟功能,无需手机即可测试。
/// </summary>
public class RhythmInputManager : MonoBehaviour
{
// =====================================================================
// 公共事件 (Public Events)
// =====================================================================
public static event Action<int, Vector2> OnTap;
public static event Action<int, Vector2> OnTouch;
public static event Action<int, Vector2, Vector2> OnSwipe;
// =====================================================================
// 可配置参数 (Configurable Parameters)
// =====================================================================
[Header("划动设置 (Swipe Settings)")]
[Tooltip("识别为划动的最小移动距离(像素)")]
[SerializeField] private float minSwipeDistance = 50.0f;
// =====================================================================
// 内部状态 (Internal State)
// =====================================================================
private class TouchState
{
public int TouchId;
public Vector2 StartPosition;
public float StartTime;
public Vector2 LastSwipeDirection = Vector2.zero;
public bool IsTapCandidate = true;
}
private readonly Dictionary<int, TouchState> _activeTouches = new Dictionary<int, TouchState>();
// 为鼠标模拟专门设置一个固定的Touch ID
private const int MOUSE_TOUCH_ID = 999;
// =====================================================================
// MonoBehaviour 生命周期方法 (Lifecycle Methods)
// =====================================================================
private void Awake()
{
//OnTap += (id, pos)=> Debug.Log($"Tap Detected: Touch ID {id}, Position {pos}");
//OnTouch += (id, pos) => Debug.Log($"Touch Detected: Touch ID {id}, Position {pos}");
//OnSwipe += (id, pos, dir) => Debug.Log($"Swipe Detected: Touch ID {id}, Position {pos}, Direction {dir}");
}
private void Update()
{
// 使用预处理指令区分平台
#if UNITY_EDITOR
// --- 在Unity编辑器中使用鼠标模拟触摸 ---
ProcessMouseInput();
#else
// --- 在真机设备上,使用真实的触摸输入 ---
ProcessRealTouchInput();
#endif
}
// =====================================================================
// 核心处理逻辑 (Core Processing Logic)
// =====================================================================
#if UNITY_EDITOR
/// <summary>
/// 【仅在编辑器中运行】处理鼠标输入并模拟触摸事件。
/// </summary>
private void ProcessMouseInput()
{
if (Mouse.current == null) return;
Vector2 position = Mouse.current.position.ReadValue();
if (Mouse.current.leftButton.wasPressedThisFrame)
{
ProcessInputEvent(MOUSE_TOUCH_ID, TouchPhase.Began, position);
}
else if (Mouse.current.leftButton.isPressed)
{
// 如果鼠标位置有变化则为Moved否则为Stationary
if (Mouse.current.delta.ReadValue().sqrMagnitude > 0.1f)
{
ProcessInputEvent(MOUSE_TOUCH_ID, TouchPhase.Moved, position);
}
else
{
ProcessInputEvent(MOUSE_TOUCH_ID, TouchPhase.Stationary, position);
}
}
else if (Mouse.current.leftButton.wasReleasedThisFrame)
{
ProcessInputEvent(MOUSE_TOUCH_ID, TouchPhase.Ended, position);
}
if (Mouse.current.rightButton.wasPressedThisFrame)
{
ProcessInputEvent(MOUSE_TOUCH_ID + 1, TouchPhase.Began, position);
}
else if (Mouse.current.rightButton.isPressed)
{
// 如果鼠标位置有变化则为Moved否则为Stationary
if (Mouse.current.delta.ReadValue().sqrMagnitude > 0.1f)
{
ProcessInputEvent(MOUSE_TOUCH_ID + 1, TouchPhase.Moved, position);
}
else
{
ProcessInputEvent(MOUSE_TOUCH_ID + 1, TouchPhase.Stationary, position);
}
}
else if (Mouse.current.rightButton.wasReleasedThisFrame)
{
ProcessInputEvent(MOUSE_TOUCH_ID + 1, TouchPhase.Ended, position);
}
}
#endif
/// <summary>
/// 【仅在真机上运行】处理真实的触摸屏输入。
/// </summary>
private void ProcessRealTouchInput()
{
if (Touchscreen.current == null) return;
foreach (Touch touch in Touch.activeTouches)
{
ProcessInputEvent(
touch.touchId,
touch.phase,
touch.screenPosition
);
}
}
/// <summary>
/// 所有输入事件的核心处理函数,无论是真实触摸还是鼠标模拟都会调用它。
/// </summary>
private void ProcessInputEvent(int touchId, TouchPhase phase, Vector2 position)
{
switch (phase)
{
case TouchPhase.Began:
var newState = new TouchState
{
TouchId = touchId,
StartPosition = position,
StartTime = Time.time,
};
_activeTouches[touchId] = newState;
OnTap?.Invoke(touchId, position);
OnTouch?.Invoke(touchId, position);
break;
case TouchPhase.Moved:
if (_activeTouches.TryGetValue(touchId, out TouchState movedState))
{
OnTouch?.Invoke(touchId, position);
DetectSwipe(movedState, position);
}
break;
case TouchPhase.Stationary:
if (_activeTouches.TryGetValue(touchId, out TouchState stationaryState))
{
OnTouch?.Invoke(touchId, position);
}
break;
case TouchPhase.Canceled:
if (_activeTouches.ContainsKey(touchId))
{
_activeTouches.Remove(touchId);
}
break;
}
}
/// <summary>
/// 检测划动逻辑 (无需修改)
/// </summary>
private void DetectSwipe(TouchState state, Vector2 currentPosition)
{
Vector2 swipeVector = currentPosition - state.StartPosition;
if (swipeVector.magnitude < minSwipeDistance) return;
Vector2 direction = swipeVector.normalized;
// 检查是否是新的划动方向
if (Vector2.Dot(direction, state.LastSwipeDirection) < 0.5f)
{
OnSwipe?.Invoke(state.TouchId, state.StartPosition, direction);
state.LastSwipeDirection = direction;
state.StartPosition = currentPosition;
state.StartTime = Time.time;
}
}
}