@@ -2,6 +2,13 @@ using UnityEngine;
using UnityEngine.InputSystem ;
using System ;
using System.Collections.Generic ;
using DG.Tweening ;
using Ichni ;
using Lean.Common ;
using Lean.Pool ;
using Sirenix.OdinInspector ;
using UnityEngine.InputSystem.Controls ;
using UnityEngine.UI ;
using Touch = UnityEngine . InputSystem . EnhancedTouch . Touch ;
using TouchPhase = UnityEngine . InputSystem . TouchPhase ;
@@ -9,14 +16,14 @@ using TouchPhase = UnityEngine.InputSystem.TouchPhase;
/// 为节奏游戏设计的输入管理器,处理多点触控并分发三种主要事件。
/// 【重要】此版本内置了编辑器内的鼠标模拟功能,无需手机即可测试。
/// </summary>
public class RhythmInputManager : MonoBehaviour
public class RhythmInputManager : Serialized 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 ;
public static event Action < int , Vector2 , bool , Vector2 > OnSwipe ;
// =====================================================================
// 可配置参数 (Configurable Parameters)
@@ -24,7 +31,8 @@ public class RhythmInputManager : MonoBehaviour
[Header("划动设置 (Swipe Settings)")]
[Tooltip("识别为划动的最小移动距离(像素)")]
[SerializeField] private float minSwipeDistance = 5 0.0f;
[SerializeField] private float minSwipeDistance = 20 0.0f;
[SerializeField] private float swipeAngleThreshold = 1f ;
// =====================================================================
// 内部状态 (Internal State)
@@ -52,14 +60,19 @@ public class RhythmInputManager : MonoBehaviour
//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}");
OnTap + = GenerateTapMark ;
OnTouch + = GenerateTouchMark ;
OnSwipe + = GenerateSwipeMark ;
}
private void Update ( )
{
// 使用预处理指令区分平台
#if UNITY_EDITOR
#if UNITY_EDITOR | | UNITY_STANDALONE
// --- 在Unity编辑器中, 使用鼠标模拟触摸 ---
ProcessMouseInput( ) ;
// ProcessMouseInput() ;
ProcessKeyboardInput ( ) ;
#else
// --- 在真机设备上,使用真实的触摸输入 ---
ProcessRealTouchInput ( ) ;
@@ -124,6 +137,48 @@ public class RhythmInputManager : MonoBehaviour
}
#endif
#if UNITY_EDITOR | | UNITY_STANDALONE
public static List < KeyControl > availableKeys = new List < KeyControl > ( )
{
Keyboard . current . dKey ,
Keyboard . current . fKey ,
Keyboard . current . jKey ,
Keyboard . current . kKey ,
} ;
public static List < KeyControl > flickKeys = new List < KeyControl > ( )
{
Keyboard . current . sKey ,
Keyboard . current . lKey ,
} ;
private void ProcessKeyboardInput ( )
{
for ( int index = 0 ; index < availableKeys . Count ; index + + )
{
Vector2 inputPosition = new Vector2 ( index * 400 - 600 + Screen . width * 0.5f , 200f ) ; // 假设每个按键的虚拟位置
if ( availableKeys [ index ] . wasPressedThisFrame )
{
OnTap ? . Invoke ( index , inputPosition ) ;
}
if ( availableKeys [ index ] . isPressed )
{
OnTouch ? . Invoke ( index , inputPosition ) ;
}
}
for ( int index = 0 ; index < flickKeys . Count ; index + + )
{
Vector2 inputPosition = new Vector2 ( index * 1600 - 800 + Screen . width * 0.5f , 200f ) ; // 假设每个划动按键的虚拟位置
if ( flickKeys [ index ] . wasPressedThisFrame )
{
OnSwipe ? . Invoke ( index , inputPosition , true , Vector2 . zero ) ;
}
}
}
#endif
/// <summary>
/// 【仅在真机上运行】处理真实的触摸屏输入。
/// </summary>
@@ -198,12 +253,86 @@ public class RhythmInputManager : MonoBehaviour
Vector2 direction = swipeVector . normalized ;
// 检查是否是新的划动方向
if ( Vector2 . Dot ( direction , state . LastSwipeDirection ) < 0.5f )
if ( Vector2 . Dot ( direction , state . LastSwipeDirection ) < = swipeAngleThreshold )
{
OnSwipe ? . Invoke ( state . TouchId , state . StartPosition , direction ) ;
OnSwipe ? . Invoke ( state . TouchId , state . StartPosition , false , direction ) ;
state . LastSwipeDirection = direction ;
state . StartPosition = currentPosition ;
state . StartTime = Time . time ;
}
}
private void GenerateTapMark ( int id , Vector2 pos )
{
RectTransform mark = LeanPool . Spawn ( GameManager . instance . basePrefabs . tapInputMark ,
GameManager . instance . judgeHintCanvas . transform ) . GetComponent < RectTransform > ( ) ;
RectTransform canvasRect = GameManager . instance . judgeHintCanvas . GetComponent < RectTransform > ( ) ;
if ( RectTransformUtility . ScreenPointToLocalPointInRectangle ( canvasRect , pos , null , out Vector2 uiPosition ) )
{
mark . anchoredPosition = uiPosition ;
}
Sequence ss = DOTween . Sequence ( ) ;
ss . OnStart ( ( ) = >
{
mark . GetComponent < Image > ( ) . color = Color . white ;
mark . localScale = Vector3 . zero ;
} ) ;
ss . Join ( mark . GetComponent < Image > ( ) . DOFade ( 0 , 0.5f ) ) ;
ss . Join ( mark . DOScale ( 5 , 0.5f ) ) ;
ss . OnComplete ( ( ) = > LeanPool . Despawn ( mark . gameObject ) ) ;
ss . SetUpdate ( true ) ;
ss . Play ( ) ;
}
private void GenerateTouchMark ( int id , Vector2 pos )
{
RectTransform mark = LeanPool . Spawn ( GameManager . instance . basePrefabs . touchInputMark ,
GameManager . instance . judgeHintCanvas . transform ) . GetComponent < RectTransform > ( ) ;
RectTransform canvasRect = GameManager . instance . judgeHintCanvas . GetComponent < RectTransform > ( ) ;
if ( RectTransformUtility . ScreenPointToLocalPointInRectangle ( canvasRect , pos , null , out Vector2 uiPosition ) )
{
mark . anchoredPosition = uiPosition ;
}
Sequence ss = DOTween . Sequence ( ) ;
ss . OnStart ( ( ) = >
{
mark . GetComponent < Image > ( ) . color = Color . white ;
} ) ;
ss . Join ( mark . GetComponent < Image > ( ) . DOFade ( 0 , 0.1f ) ) ;
ss . OnComplete ( ( ) = > LeanPool . Despawn ( mark . gameObject ) ) ;
ss . SetUpdate ( true ) ;
ss . Play ( ) ;
}
private void GenerateSwipeMark ( int id , Vector2 pos , bool isGeneric , Vector2 direction )
{
GameObject markPrefab = isGeneric
? GameManager . instance . basePrefabs . genericSwipeInputMark
: GameManager . instance . basePrefabs . directionalSwipeInputMark ;
RectTransform mark = LeanPool . Spawn ( markPrefab , GameManager . instance . judgeHintCanvas . transform ) . GetComponent < RectTransform > ( ) ;
RectTransform canvasRect = GameManager . instance . judgeHintCanvas . GetComponent < RectTransform > ( ) ;
if ( RectTransformUtility . ScreenPointToLocalPointInRectangle ( canvasRect , pos , null , out Vector2 uiPosition ) )
{
mark . anchoredPosition = uiPosition ;
}
mark . localEulerAngles = new Vector3 ( 0 , 0 , Mathf . Atan2 ( direction . y , direction . x ) * Mathf . Rad2Deg - 90f ) ;
Sequence ss = DOTween . Sequence ( ) ;
ss . OnStart ( ( ) = >
{
mark . GetComponent < Image > ( ) . color = Color . white ;
mark . localScale = Vector3 . zero ;
} ) ;
ss . Join ( mark . GetComponent < Image > ( ) . DOFade ( 0 , 0.5f ) ) ;
ss . Join ( mark . DOScale ( 5 , 0.5f ) ) ;
ss . OnComplete ( ( ) = > LeanPool . Despawn ( mark . gameObject ) ) ;
ss . SetUpdate ( true ) ;
ss . Play ( ) ;
}
}