feat(shock): 拉杆接入EventTriggerEx并统一光标
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,16 +1,20 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using DG.Tweening;
|
||||
using TMPro;
|
||||
using RainbowArt.CleanFlatUI;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using AibisDream.Framework;
|
||||
using UnityEngine.EventSystems;
|
||||
using System.Linq;
|
||||
using AibisDream;
|
||||
using AibisDream.FixSystem;
|
||||
using System.Collections;
|
||||
using AibisDream; // 添加 SpriteFlashSystem 的命名空间
|
||||
using AibisDream.Framework;
|
||||
using DG.Tweening;
|
||||
using RainbowArt.CleanFlatUI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class ShockSystem : MonoBehaviour
|
||||
/// <summary>
|
||||
/// 电击器系统。拉杆交互通过 EventTriggerEx 统一管理,支持 EventSystemEx.isLocked 与 IInteraction,
|
||||
/// 光标由 EventTriggerEx → EventSystemEx → CursorManager 自动驱动。
|
||||
/// </summary>
|
||||
public class ShockSystem : MonoBehaviour, IInteraction
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] private Transform shockDevice; // 电击器
|
||||
@@ -98,18 +102,74 @@ public class ShockSystem : MonoBehaviour
|
||||
private Tweener abnormalLeverShakeTweener; // 异常状态下拉杆震动动画
|
||||
private Tweener abnormalPanelShakeTweener; // 异常状态下面板震动动画
|
||||
private Color normalProgressBarColor; // 正常状态下进度条颜色
|
||||
private EventTriggerEx _eventTrigger;
|
||||
private ShockLeverInteractionBridge _leverBridge;
|
||||
private bool _useEventTrigger;
|
||||
private bool _leverInputLocked;
|
||||
private bool _leverPointerOver;
|
||||
|
||||
// 公共访问器
|
||||
public float FlashDuration => flashDuration;
|
||||
public float FlashInterval => flashInterval;
|
||||
public int FlashCount => flashCount;
|
||||
|
||||
#region IInteraction
|
||||
|
||||
public bool IsActive => true;
|
||||
public bool IsAvailable => !isAbnormalState && !_leverInputLocked;
|
||||
public GameObject GetGameObject() => gameObject;
|
||||
|
||||
#endregion
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
FixSystemCenter.SystemDic.Register(this);
|
||||
EnsureLeverInteraction();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为拉杆挂接 ShockLeverInteractionBridge + EventTriggerEx(对齐 CrankController)。
|
||||
/// 悬停光标另用 LateUpdate 射线校正:指针 Collider 细长且会旋转,EventSystem 易丢 PointerExit。
|
||||
/// </summary>
|
||||
private void EnsureLeverInteraction()
|
||||
{
|
||||
if (lever == null) return;
|
||||
|
||||
var leverGo = lever.gameObject;
|
||||
if (leverGo.GetComponent<Collider2D>() == null)
|
||||
{
|
||||
// 注册
|
||||
FixSystemCenter.SystemDic.Register(this);
|
||||
#if UNITY_EDITOR
|
||||
Debug.LogWarning("[ShockSystem] 拉杆未找到 Collider2D!请添加 Collider2D 以接入交互与光标。", this);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
_leverBridge = leverGo.GetComponent<ShockLeverInteractionBridge>();
|
||||
if (_leverBridge == null)
|
||||
_leverBridge = leverGo.AddComponent<ShockLeverInteractionBridge>();
|
||||
|
||||
_eventTrigger = leverGo.GetComponent<EventTriggerEx>();
|
||||
if (_eventTrigger == null)
|
||||
_eventTrigger = leverGo.AddComponent<EventTriggerEx>();
|
||||
|
||||
_useEventTrigger = true;
|
||||
EnsureEventTriggerEntries();
|
||||
_eventTrigger.Register(EventTriggerType.PointerDown, OnEventPointerDown);
|
||||
_eventTrigger.Register(EventTriggerType.PointerUp, OnEventPointerUp);
|
||||
_eventTrigger.Register(EventTriggerType.Drag, OnEventDrag);
|
||||
}
|
||||
|
||||
private void EnsureEventTriggerEntries()
|
||||
{
|
||||
if (_eventTrigger == null) return;
|
||||
var triggers = _eventTrigger.triggers;
|
||||
var needed = new[] { EventTriggerType.PointerDown, EventTriggerType.PointerUp, EventTriggerType.Drag };
|
||||
foreach (var t in needed)
|
||||
{
|
||||
if (triggers.Any(e => e.eventID == t)) continue;
|
||||
triggers.Add(new EventTrigger.Entry { eventID = t });
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
@@ -152,77 +212,183 @@ public class ShockSystem : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleLeverInput();
|
||||
if (_useEventTrigger)
|
||||
UpdateLeverReturn();
|
||||
else
|
||||
HandleLeverInputFallback();
|
||||
|
||||
UpdateLeverRotation();
|
||||
UpdateCharging();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleLeverInput()
|
||||
private void LateUpdate()
|
||||
{
|
||||
SyncLeverHoverCursor();
|
||||
}
|
||||
|
||||
#region 拉杆输入(EventTriggerEx 驱动 / 回退)
|
||||
|
||||
private bool IsMouseOverLever()
|
||||
{
|
||||
if (lever == null) return false;
|
||||
var cam = Camera.main;
|
||||
if (cam == null) return false;
|
||||
|
||||
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
|
||||
RaycastHit2D hit = Physics2D.GetRayIntersection(ray, float.MaxValue);
|
||||
if (hit.collider != null && hit.collider.transform == lever)
|
||||
return true;
|
||||
|
||||
hit = Physics2D.Raycast(ray.origin, ray.direction);
|
||||
return hit.collider != null && hit.collider.transform == lever;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 以射线结果校正悬停光标,弥补细长旋转 Collider 丢失 PointerExit 的情况。
|
||||
/// </summary>
|
||||
private void SyncLeverHoverCursor()
|
||||
{
|
||||
if (_leverBridge == null || EventSystemEx.Instance == null) return;
|
||||
|
||||
bool over = IsAvailable && IsMouseOverLever();
|
||||
if (over == _leverPointerOver) return;
|
||||
|
||||
_leverPointerOver = over;
|
||||
if (over)
|
||||
EventSystemEx.Instance.OnPointerEnter(_leverBridge);
|
||||
else
|
||||
EventSystemEx.Instance.OnPointerExit(_leverBridge);
|
||||
}
|
||||
|
||||
private void OnEventPointerDown(BaseEventData eventData)
|
||||
{
|
||||
if (!IsAvailable || (EventSystemEx.Instance != null && EventSystemEx.Instance.isLocked)) return;
|
||||
if (eventData is not PointerEventData { button: PointerEventData.InputButton.Left } pointerData) return;
|
||||
|
||||
isDraggingLever = true;
|
||||
lastMousePosition = pointerData.position;
|
||||
}
|
||||
|
||||
private void OnEventPointerUp(BaseEventData eventData)
|
||||
{
|
||||
if (eventData is not PointerEventData { button: PointerEventData.InputButton.Left }) return;
|
||||
EndLeverDrag();
|
||||
}
|
||||
|
||||
private void OnEventDrag(BaseEventData eventData)
|
||||
{
|
||||
if (!isDraggingLever || !IsAvailable || (EventSystemEx.Instance != null && EventSystemEx.Instance.isLocked)) return;
|
||||
if (eventData is not PointerEventData pointerData) return;
|
||||
|
||||
ApplyLeverDrag(pointerData.position);
|
||||
}
|
||||
|
||||
/// <summary>无 EventTriggerEx 时回退到 Input + Raycast。</summary>
|
||||
private void HandleLeverInputFallback()
|
||||
{
|
||||
if (!IsAvailable || (EventSystemEx.Instance != null && EventSystemEx.Instance.isLocked))
|
||||
{
|
||||
UpdateLeverReturn();
|
||||
return;
|
||||
}
|
||||
|
||||
if (Input.GetMouseButtonDown(0))
|
||||
{
|
||||
// 检查是否点击到杆
|
||||
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||||
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);
|
||||
|
||||
if (hit.collider != null && hit.collider.transform == lever)
|
||||
var cam = Camera.main;
|
||||
if (cam != null && lever != null)
|
||||
{
|
||||
isDraggingLever = true;
|
||||
lastMousePosition = Input.mousePosition;
|
||||
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
|
||||
RaycastHit2D hit = Physics2D.GetRayIntersection(ray, float.MaxValue);
|
||||
if (hit.collider != null && hit.collider.transform == lever)
|
||||
{
|
||||
isDraggingLever = true;
|
||||
lastMousePosition = Input.mousePosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Input.GetMouseButtonUp(0))
|
||||
{
|
||||
isDraggingLever = false;
|
||||
// 停止充能
|
||||
if (isCharging)
|
||||
{
|
||||
isCharging = false;
|
||||
if (currentProgress >= progressBar.MaxValue)
|
||||
{
|
||||
DialogController.Instance.StartDialogNode(shockEffectNode);
|
||||
}
|
||||
ResetProgress();
|
||||
}
|
||||
EndLeverDrag();
|
||||
}
|
||||
|
||||
if (isDraggingLever)
|
||||
{
|
||||
Vector3 mouseDelta = Input.mousePosition - lastMousePosition;
|
||||
float angleDelta = mouseDelta.x * 0.5f; // 调整灵敏度
|
||||
|
||||
targetLeverAngle = Mathf.Clamp(targetLeverAngle + angleDelta, 0f, maxLeverAngle);
|
||||
lastMousePosition = Input.mousePosition;
|
||||
|
||||
// 根据杆的角度计算进度
|
||||
float progressRatio = targetLeverAngle / maxLeverAngle;
|
||||
if (progressRatio > 0 && !isCharging)
|
||||
{
|
||||
isCharging = true;
|
||||
StartCharging();
|
||||
// 开始播放充能音效
|
||||
AibisDream.Kit.AudioManager.Instance.PlaySfx(chargingEvent, chargingAudioKey);
|
||||
}
|
||||
}
|
||||
ApplyLeverDrag(Input.mousePosition);
|
||||
else
|
||||
UpdateLeverReturn();
|
||||
}
|
||||
|
||||
private void ApplyLeverDrag(Vector3 mousePosition)
|
||||
{
|
||||
Vector3 mouseDelta = mousePosition - lastMousePosition;
|
||||
float angleDelta = mouseDelta.x * 0.5f;
|
||||
targetLeverAngle = Mathf.Clamp(targetLeverAngle + angleDelta, 0f, maxLeverAngle);
|
||||
lastMousePosition = mousePosition;
|
||||
|
||||
float progressRatio = targetLeverAngle / maxLeverAngle;
|
||||
if (progressRatio > 0 && !isCharging)
|
||||
{
|
||||
// 杆自动回弹
|
||||
targetLeverAngle = Mathf.MoveTowards(targetLeverAngle, 0f, leverReturnSpeed * Time.deltaTime);
|
||||
|
||||
// 如果杆已经回弹到接近0度,确保停止充能
|
||||
if (targetLeverAngle < 0.1f && isCharging)
|
||||
{
|
||||
isCharging = false;
|
||||
if (currentProgress >= progressBar.MaxValue)
|
||||
{
|
||||
DialogController.Instance.StartDialogNode(shockEffectNode);
|
||||
}
|
||||
ResetProgress();
|
||||
}
|
||||
isCharging = true;
|
||||
StartCharging();
|
||||
AibisDream.Kit.AudioManager.Instance.PlaySfx(chargingEvent, chargingAudioKey);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLeverReturn()
|
||||
{
|
||||
if (isDraggingLever) return;
|
||||
|
||||
targetLeverAngle = Mathf.MoveTowards(targetLeverAngle, 0f, leverReturnSpeed * Time.deltaTime);
|
||||
|
||||
if (targetLeverAngle < 0.1f && isCharging)
|
||||
{
|
||||
isCharging = false;
|
||||
if (currentProgress >= progressBar.MaxValue)
|
||||
DialogController.Instance.StartDialogNode(shockEffectNode);
|
||||
ResetProgress();
|
||||
}
|
||||
}
|
||||
|
||||
private void EndLeverDrag()
|
||||
{
|
||||
if (!isDraggingLever && !isCharging)
|
||||
{
|
||||
isDraggingLever = false;
|
||||
return;
|
||||
}
|
||||
|
||||
isDraggingLever = false;
|
||||
if (!isCharging) return;
|
||||
|
||||
isCharging = false;
|
||||
bool shouldShock = currentProgress >= progressBar.MaxValue;
|
||||
ResetProgress();
|
||||
|
||||
// 先清交互光标状态,再开对话(DialogStart 会 isLocked,旧逻辑会跳过 Up/EndDrag 清理)
|
||||
if (_leverBridge != null && EventSystemEx.Instance != null)
|
||||
EventSystemEx.Instance.UnRegister(_leverBridge);
|
||||
_leverPointerOver = false;
|
||||
|
||||
if (shouldShock)
|
||||
DialogController.Instance.StartDialogNode(shockEffectNode);
|
||||
}
|
||||
|
||||
private void ForceClearLeverInteraction()
|
||||
{
|
||||
isDraggingLever = false;
|
||||
if (isCharging)
|
||||
{
|
||||
isCharging = false;
|
||||
ResetProgress();
|
||||
}
|
||||
|
||||
if (_leverBridge != null && EventSystemEx.Instance != null)
|
||||
EventSystemEx.Instance.UnRegister(_leverBridge);
|
||||
_leverPointerOver = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void UpdateLeverRotation()
|
||||
{
|
||||
if (lever != null)
|
||||
@@ -486,12 +652,11 @@ public class ShockSystem : MonoBehaviour
|
||||
|
||||
if (isAbnormal)
|
||||
{
|
||||
// 启动异常状态效果
|
||||
ForceClearLeverInteraction();
|
||||
StartAbnormalEffects();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 停止异常状态效果
|
||||
StopAbnormalEffects();
|
||||
}
|
||||
}
|
||||
@@ -589,15 +754,10 @@ public class ShockSystem : MonoBehaviour
|
||||
// 自动充能并释放
|
||||
public IEnumerator AutoChargeAndRelease(float duration = 1f)
|
||||
{
|
||||
// 保存当前异常状态
|
||||
bool originalAbnormalState = isAbnormalState;
|
||||
|
||||
// 临时禁用异常状态
|
||||
isAbnormalState = false;
|
||||
|
||||
// 禁用玩家输入
|
||||
bool originalIsDraggingLever = isDraggingLever;
|
||||
isDraggingLever = false;
|
||||
_leverInputLocked = true;
|
||||
ForceClearLeverInteraction();
|
||||
|
||||
// 设置杆的角度
|
||||
targetLeverAngle = maxLeverAngle;
|
||||
@@ -625,7 +785,7 @@ public class ShockSystem : MonoBehaviour
|
||||
// 恢复原始设置
|
||||
progressIncreaseSpeed = originalProgressIncreaseSpeed;
|
||||
isCharging = false;
|
||||
isDraggingLever = originalIsDraggingLever;
|
||||
_leverInputLocked = false;
|
||||
|
||||
// 重置杆的角度
|
||||
targetLeverAngle = 0f;
|
||||
@@ -643,6 +803,15 @@ public class ShockSystem : MonoBehaviour
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_eventTrigger != null)
|
||||
{
|
||||
_eventTrigger.UnRegister(EventTriggerType.PointerDown, OnEventPointerDown);
|
||||
_eventTrigger.UnRegister(EventTriggerType.PointerUp, OnEventPointerUp);
|
||||
_eventTrigger.UnRegister(EventTriggerType.Drag, OnEventDrag);
|
||||
}
|
||||
|
||||
ForceClearLeverInteraction();
|
||||
|
||||
if (progressTweener != null)
|
||||
{
|
||||
progressTweener.Kill();
|
||||
@@ -698,4 +867,21 @@ public class ShockSystem : MonoBehaviour
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 电击器拉杆交互桥接:挂在 Collider 所在子物体上,将 IInteraction 转交给父级 ShockSystem(对齐 CrankInteractionBridge)。
|
||||
/// </summary>
|
||||
public class ShockLeverInteractionBridge : MonoBehaviour, IInteraction
|
||||
{
|
||||
private ShockSystem _shockSystem;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_shockSystem = GetComponentInParent<ShockSystem>();
|
||||
}
|
||||
|
||||
public bool IsActive => _shockSystem?.IsActive ?? true;
|
||||
public bool IsAvailable => _shockSystem?.IsAvailable ?? true;
|
||||
public GameObject GetGameObject() => gameObject;
|
||||
}
|
||||
Reference in New Issue
Block a user