Enter 中先 LoadBubbles 再 SwitchCamera,避免 me: 释放log 时找不到 Player 槽位。 SalesState 中优先使用 salesManager.bubbleSlotGroup,缺失时回退 ScreenSystem。 Made-with: Cursor
645 lines
24 KiB
C#
645 lines
24 KiB
C#
using System.Linq;
|
||
using AibisDream.Framework;
|
||
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
using UnityEngine.EventSystems;
|
||
using DG.Tweening;
|
||
using System.Collections;
|
||
|
||
namespace AibisDream.MiniGame.HuoShan
|
||
{
|
||
/// <summary>
|
||
/// 发条控制器 - 实现发条的拖拽旋转交互
|
||
/// 功能:拖拽旋转,最大260°,缓慢回位,触发事件
|
||
/// 交互通过 EventTriggerEx 统一管理,支持 EventSystemEx.isLocked 和 IInteraction 状态
|
||
/// 在发条 GameObject 上添加 EventTriggerEx + Collider2D 即可接入;未添加则回退到 Input+Raycast
|
||
/// </summary>
|
||
public class CrankController : MonoBehaviour, IInteraction
|
||
{
|
||
[Header("发条配置")]
|
||
[Tooltip("最大旋转角度(度)")]
|
||
[SerializeField] private float maxRotationAngle = 260f;
|
||
|
||
[Tooltip("回位时长(秒),从满弦回到 0 的匀速时间,与滑片滑动时长匹配")]
|
||
[SerializeField] private float returnDuration = 3f;
|
||
|
||
[Tooltip("触发事件的最小角度(度)")]
|
||
[SerializeField] private float triggerAngle = 240f;
|
||
|
||
[Tooltip("拖拽阻力,值越大越难拖动")]
|
||
[SerializeField] private float dragResistance = 0.1f;
|
||
|
||
[Tooltip("是否自动返回初始位置")]
|
||
[SerializeField] private bool autoReturn = true;
|
||
|
||
[Header("事件" )]
|
||
[Tooltip("发条达到最大角度并释放时触发")]
|
||
public UnityEvent onCrankComplete;
|
||
|
||
[Tooltip("发条旋转时触发(参数:当前角度0-1)")]
|
||
public UnityEvent<float> onCrankRotate;
|
||
|
||
[Tooltip("发条开始拖拽时触发")]
|
||
public UnityEvent onCrankStartDrag;
|
||
|
||
[Header("逆时针转圈表现")]
|
||
[Tooltip("逆时针转一圈的动画时长(秒)")]
|
||
[SerializeField] private float spinOnceDuration = 1f;
|
||
|
||
[Tooltip("逆时针转一圈完成后触发")]
|
||
public UnityEvent onSpinOnceComplete;
|
||
|
||
[Header("圆环/中心配置")]
|
||
[Tooltip("环中心(内环旋转中心),配置后发条将沿环轨道移动;不填则原地旋转")]
|
||
[SerializeField] private Transform ringCenter;
|
||
|
||
[Tooltip("环半径(发条到中心的距离),0 则从初始位置自动计算")]
|
||
[SerializeField] private float ringRadius = 0f;
|
||
|
||
[Tooltip("起始角度偏移(度),0=右侧3点钟方向")]
|
||
[SerializeField] private float startAngleOffset = 0f;
|
||
|
||
[Tooltip("面向圆心时的旋转偏移(度),用于调整图片朝向,不同 sprite 的正面方向不同")]
|
||
[SerializeField] private float facingRotationOffset = 0f;
|
||
|
||
[Tooltip("角度正方向:勾选=顺时针为正,不勾选=逆时针为正")]
|
||
[SerializeField] private bool clockwisePositive = false;
|
||
|
||
[Header("视觉反馈")]
|
||
[Tooltip("发条旋转的 Transform(手柄 visuals)")]
|
||
[SerializeField] private Transform crankTransform;
|
||
|
||
[Tooltip("用于检测点击的 Collider2D,不填则尝试从 crankTransform 获取")]
|
||
[SerializeField] private Collider2D interactionCollider;
|
||
|
||
[Tooltip("拖拽时的缩放效果")]
|
||
[SerializeField] private float dragScale = 1.05f;
|
||
|
||
[Tooltip("缩放动画时长")]
|
||
[SerializeField] private float scaleDuration = 0.1f;
|
||
|
||
private Vector3 _dragStartPosition;
|
||
private float _dragStartAngle; // 按下时鼠标相对中心的角度
|
||
private float _dragStartRotation; // 按下时发条的角度
|
||
private float _lastMouseAngle; // 上一帧鼠标角度,用于累计增量
|
||
private float _currentRotation = 0f;
|
||
private bool _isDragging = false;
|
||
private bool _hasTriggered = false;
|
||
private Camera _mainCamera;
|
||
private Vector3 _initialScale;
|
||
private Tweener _scaleTween;
|
||
private Tweener _returnTween;
|
||
private Tweener _spinTween;
|
||
private float _spinAngleOffset; // 逆时针转圈时的附加角度(0~360)
|
||
private bool _isSpinning; // 正在播放逆时针转圈
|
||
private float _effectiveRadius; // 实际使用的环半径
|
||
private bool _isReturning;
|
||
private float _returnSpeedPerSec; // 回位时的度/秒,匀速
|
||
|
||
private EventTriggerEx _eventTrigger;
|
||
private bool _useEventTrigger;
|
||
|
||
#region 生命周期
|
||
|
||
private void Awake()
|
||
{
|
||
_mainCamera = Camera.main;
|
||
if (crankTransform != null)
|
||
{
|
||
_initialScale = crankTransform.localScale;
|
||
}
|
||
else
|
||
{
|
||
_initialScale = transform.localScale;
|
||
crankTransform = transform;
|
||
}
|
||
|
||
// 确保触发角度不超过最大旋转角度
|
||
if (triggerAngle > maxRotationAngle)
|
||
{
|
||
Debug.LogWarning($"[CrankController] triggerAngle ({triggerAngle}°) 超过 maxRotationAngle ({maxRotationAngle}°),已自动调整为 {maxRotationAngle * 0.8f:F0}°", this);
|
||
triggerAngle = maxRotationAngle * 0.8f;
|
||
}
|
||
|
||
// 未配置交互碰撞体时,尝试从 crankTransform 获取
|
||
if (interactionCollider == null && crankTransform != null)
|
||
{
|
||
interactionCollider = crankTransform.GetComponent<Collider2D>();
|
||
#if UNITY_EDITOR
|
||
if (interactionCollider == null)
|
||
{
|
||
Debug.LogWarning($"[CrankController] 未找到 Collider2D!请在「发条」或其父物体上添加 Collider2D(如 CircleCollider2D),或在 interactionCollider 中手动指定。", this);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
// 计算环半径:优先使用配置值,否则从发条到 ringCenter 的距离
|
||
if (ringCenter != null && crankTransform != null)
|
||
{
|
||
_effectiveRadius = ringRadius > 0f
|
||
? ringRadius
|
||
: Vector2.Distance(
|
||
new Vector2(crankTransform.position.x, crankTransform.position.y),
|
||
new Vector2(ringCenter.position.x, ringCenter.position.y));
|
||
if (_effectiveRadius < 0.01f) _effectiveRadius = 1f; // 防止除零
|
||
}
|
||
else
|
||
{
|
||
_effectiveRadius = 0f; // 原地旋转模式
|
||
}
|
||
|
||
// 事件目标:Collider 所在的对象(可能与 CrankController 不同,如手柄在子物体)
|
||
var eventTargetGo = interactionCollider != null ? interactionCollider.gameObject : (crankTransform != null ? crankTransform.gameObject : gameObject);
|
||
|
||
_eventTrigger = eventTargetGo.GetComponent<EventTriggerEx>();
|
||
if (_eventTrigger == null && eventTargetGo.GetComponent<Collider2D>() != null)
|
||
{
|
||
if (eventTargetGo != gameObject)
|
||
{
|
||
var bridge = eventTargetGo.GetComponent<CrankInteractionBridge>();
|
||
if (bridge == null) eventTargetGo.AddComponent<CrankInteractionBridge>();
|
||
}
|
||
_eventTrigger = eventTargetGo.AddComponent<EventTriggerEx>();
|
||
}
|
||
if (_eventTrigger != null)
|
||
{
|
||
_useEventTrigger = true;
|
||
EnsureEventTriggerEntries();
|
||
_eventTrigger.Register(EventTriggerType.PointerDown, OnEventPointerDown);
|
||
_eventTrigger.Register(EventTriggerType.PointerUp, OnEventPointerUp);
|
||
_eventTrigger.Register(EventTriggerType.Drag, OnEventDrag);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 确保 EventTrigger 有需要的触发类型(若使用 EventTriggerEx 管理交互)
|
||
/// </summary>
|
||
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 OnEnable()
|
||
{
|
||
ResetCrank();
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
private void OnDrawGizmosSelected()
|
||
{
|
||
if (ringCenter == null) return;
|
||
float r = ringRadius > 0f ? ringRadius : 0f;
|
||
if (r <= 0f && crankTransform != null && !Application.isPlaying)
|
||
{
|
||
r = Vector2.Distance(
|
||
new Vector2(crankTransform.position.x, crankTransform.position.y),
|
||
new Vector2(ringCenter.position.x, ringCenter.position.y));
|
||
}
|
||
if (r < 0.01f) return;
|
||
|
||
Vector3 center = ringCenter.position;
|
||
Gizmos.color = new Color(0.3f, 0.9f, 0.55f, 0.6f);
|
||
const int segments = 64;
|
||
for (int i = 0; i < segments; i++)
|
||
{
|
||
float a0 = (float)i / segments * 2f * Mathf.PI;
|
||
float a1 = (float)(i + 1) / segments * 2f * Mathf.PI;
|
||
Vector3 p0 = center + new Vector3(Mathf.Cos(a0) * r, Mathf.Sin(a0) * r, center.z);
|
||
Vector3 p1 = center + new Vector3(Mathf.Cos(a1) * r, Mathf.Sin(a1) * r, center.z);
|
||
Gizmos.DrawLine(p0, p1);
|
||
}
|
||
// 起始位置标记
|
||
float startRadian = startAngleOffset * Mathf.Deg2Rad;
|
||
Vector3 startPos = center + new Vector3(Mathf.Cos(startRadian) * r, Mathf.Sin(startRadian) * r, center.z);
|
||
Gizmos.color = new Color(1f, 0f, 0f, 0.9f);
|
||
Gizmos.DrawWireSphere(startPos, 0.08f);
|
||
Gizmos.color = new Color(1f, 0.5f, 0f, 0.8f);
|
||
Gizmos.DrawWireSphere(center, 0.05f);
|
||
}
|
||
#endif
|
||
|
||
private void Update()
|
||
{
|
||
if (_useEventTrigger)
|
||
{
|
||
// 交互由 EventTriggerEx 驱动,此处仅处理回位
|
||
}
|
||
else
|
||
{
|
||
HandleMouseInputFallback();
|
||
}
|
||
HandleReturn();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 输入处理(EventTriggerEx 驱动 / 回退)
|
||
|
||
private void OnEventPointerDown(BaseEventData eventData)
|
||
{
|
||
if (eventData is PointerEventData { button: PointerEventData.InputButton.Left })
|
||
StartDrag();
|
||
}
|
||
|
||
private void OnEventPointerUp(BaseEventData eventData)
|
||
{
|
||
if (eventData is PointerEventData { button: PointerEventData.InputButton.Left })
|
||
EndDrag();
|
||
}
|
||
|
||
private void OnEventDrag(BaseEventData eventData)
|
||
{
|
||
if (_isDragging && eventData is PointerEventData pointerData)
|
||
ContinueDrag(pointerData.position);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 回退:无 EventTriggerEx 时使用 Input + Raycast(如 prefab 未迁移场景)
|
||
/// </summary>
|
||
private void HandleMouseInputFallback()
|
||
{
|
||
if (Input.GetMouseButtonDown(0))
|
||
{
|
||
Ray ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
|
||
RaycastHit2D hit = Physics2D.GetRayIntersection(ray, float.MaxValue);
|
||
|
||
if (hit.collider != null && IsValidHitTarget(hit.collider.gameObject))
|
||
{
|
||
StartDrag();
|
||
}
|
||
}
|
||
|
||
if (_isDragging)
|
||
{
|
||
if (Input.GetMouseButton(0))
|
||
{
|
||
ContinueDrag(Input.mousePosition);
|
||
}
|
||
else if (Input.GetMouseButtonUp(0))
|
||
{
|
||
EndDrag();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取旋转中心(环中心优先,否则用发条父节点)
|
||
/// </summary>
|
||
private Vector3 GetRotationPivot()
|
||
{
|
||
if (ringCenter != null) return ringCenter.position;
|
||
if (crankTransform != null && crankTransform.parent != null) return crankTransform.parent.position;
|
||
return crankTransform != null ? crankTransform.position : transform.position;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 应用发条的位置和旋转。配置了 ringCenter 时沿环轨道移动,否则原地旋转。
|
||
/// </summary>
|
||
private void ApplyCrankTransform(float angle)
|
||
{
|
||
if (crankTransform == null) return;
|
||
|
||
// 顺时针为正时,视觉上角度取反(数学角度逆时针为正)
|
||
float visualAngle = clockwisePositive ? -angle : angle;
|
||
float positionAngle = startAngleOffset + visualAngle + _spinAngleOffset;
|
||
|
||
if (ringCenter != null && _effectiveRadius > 0f)
|
||
{
|
||
// 沿环轨道移动:更新世界坐标位置
|
||
float radian = positionAngle * Mathf.Deg2Rad;
|
||
Vector3 center = ringCenter.position;
|
||
float z = crankTransform.position.z;
|
||
Vector3 position = center + new Vector3(
|
||
Mathf.Cos(radian) * _effectiveRadius,
|
||
Mathf.Sin(radian) * _effectiveRadius,
|
||
z
|
||
);
|
||
crankTransform.position = position;
|
||
|
||
// 始终面向圆心(径向朝内),加 offset 调整 sprite 正面朝向
|
||
Vector3 toCenter = center - position;
|
||
float radialAngle = Mathf.Atan2(toCenter.y, toCenter.x) * Mathf.Rad2Deg;
|
||
crankTransform.rotation = Quaternion.Euler(0f, 0f, radialAngle + facingRotationOffset);
|
||
}
|
||
else
|
||
{
|
||
// 原地旋转:-visualAngle 为基础朝向,+_spinAngleOffset 为逆时针转圈叠加
|
||
crankTransform.localRotation = Quaternion.Euler(0f, 0f, -visualAngle + _spinAngleOffset);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查点击目标是否有效(本物体、发条、交互碰撞体或其子物体)
|
||
/// </summary>
|
||
private bool IsValidHitTarget(GameObject hitGo)
|
||
{
|
||
if (hitGo == gameObject) return true;
|
||
if (crankTransform != null && (hitGo == crankTransform.gameObject || hitGo.transform.IsChildOf(crankTransform))) return true;
|
||
if (interactionCollider != null && hitGo == interactionCollider.gameObject) return true;
|
||
return false;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 拖拽逻辑
|
||
|
||
private void StartDrag()
|
||
{
|
||
_isDragging = true;
|
||
_dragStartPosition = Input.mousePosition;
|
||
_dragStartRotation = _currentRotation;
|
||
_dragStartAngle = GetMouseAngle();
|
||
_lastMouseAngle = _dragStartAngle;
|
||
_hasTriggered = false;
|
||
|
||
if (_returnTween != null)
|
||
{
|
||
_returnTween.Kill();
|
||
_returnTween = null;
|
||
}
|
||
if (_spinTween != null)
|
||
{
|
||
_spinTween.Kill();
|
||
_spinTween = null;
|
||
_spinAngleOffset = 0f;
|
||
_isSpinning = false;
|
||
}
|
||
_isReturning = false;
|
||
|
||
// 拖拽缩放效果
|
||
if (_scaleTween != null) _scaleTween.Kill();
|
||
_scaleTween = crankTransform.DOScale(_initialScale * dragScale, scaleDuration)
|
||
.SetEase(Ease.OutQuad);
|
||
|
||
onCrankStartDrag?.Invoke();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取鼠标相对环中心的角度(0-360)
|
||
/// </summary>
|
||
private float GetMouseAngle(Vector3? screenPos = null)
|
||
{
|
||
Vector3 pivotWorld = GetRotationPivot();
|
||
Vector3 screenCenter = _mainCamera.WorldToScreenPoint(pivotWorld);
|
||
Vector3 pos = screenPos ?? Input.mousePosition;
|
||
float a = Mathf.Atan2(pos.y - screenCenter.y, pos.x - screenCenter.x) * Mathf.Rad2Deg;
|
||
return a < 0f ? a + 360f : a;
|
||
}
|
||
|
||
private void ContinueDrag(Vector3 screenPosition)
|
||
{
|
||
// 每帧累计角度增量,支持超过 180° 的拖拽(原先用总 delta 会在 180° 处反转)
|
||
float currentAngle = GetMouseAngle(screenPosition);
|
||
float frameDelta = currentAngle - _lastMouseAngle;
|
||
|
||
// 仅对单帧小增量做 0/360 边界 wrap
|
||
if (frameDelta > 180f) frameDelta -= 360f;
|
||
else if (frameDelta < -180f) frameDelta += 360f;
|
||
|
||
_lastMouseAngle = currentAngle;
|
||
|
||
if (clockwisePositive) frameDelta = -frameDelta;
|
||
|
||
float targetRotation = _currentRotation + frameDelta * (1f - dragResistance);
|
||
|
||
// 仅当目标在范围内时更新,超出范围则保持当前位置不跟过去
|
||
if (targetRotation >= 0f && targetRotation <= maxRotationAngle)
|
||
_currentRotation = targetRotation;
|
||
|
||
ApplyCrankTransform(_currentRotation);
|
||
|
||
float normalizedRotation = _currentRotation / maxRotationAngle;
|
||
onCrankRotate?.Invoke(normalizedRotation);
|
||
|
||
// 检查是否达到触发角度
|
||
if (_currentRotation >= triggerAngle && !_hasTriggered)
|
||
{
|
||
_hasTriggered = true;
|
||
Debug.Log($"[CrankController] 达到触发角度!当前角度: {_currentRotation:F1}°, 触发角度: {triggerAngle}°", this);
|
||
}
|
||
}
|
||
|
||
private void EndDrag()
|
||
{
|
||
_isDragging = false;
|
||
|
||
// 恢复缩放
|
||
if (_scaleTween != null) _scaleTween.Kill();
|
||
_scaleTween = crankTransform.DOScale(_initialScale, scaleDuration)
|
||
.SetEase(Ease.OutQuad);
|
||
|
||
// 触发完成事件
|
||
if (_hasTriggered && _currentRotation >= triggerAngle)
|
||
{
|
||
Debug.Log($"[CrankController] EndDrag 触发完成事件,onCrankComplete 监听器数量: {onCrankComplete?.GetPersistentEventCount() ?? 0}", this);
|
||
onCrankComplete?.Invoke();
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"[CrankController] EndDrag 未触发:hasTriggered={_hasTriggered}, currentRotation={_currentRotation:F1}°, triggerAngle={triggerAngle}°", this);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 回位逻辑
|
||
|
||
private void HandleReturn()
|
||
{
|
||
if (!_isDragging && autoReturn && _currentRotation > 0f && !_isReturning && !_isSpinning)
|
||
{
|
||
_isReturning = true;
|
||
_returnSpeedPerSec = maxRotationAngle / Mathf.Max(0.01f, returnDuration);
|
||
}
|
||
|
||
if (_isReturning && !_isDragging)
|
||
{
|
||
float step = _returnSpeedPerSec * Time.deltaTime;
|
||
_currentRotation = Mathf.MoveTowards(_currentRotation, 0f, step);
|
||
ApplyCrankTransform(_currentRotation);
|
||
onCrankRotate?.Invoke(_currentRotation / maxRotationAngle);
|
||
|
||
if (_currentRotation <= 0f)
|
||
{
|
||
_currentRotation = 0f;
|
||
_isReturning = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 公共方法
|
||
|
||
public void ResetCrank()
|
||
{
|
||
if (_returnTween != null)
|
||
{
|
||
_returnTween.Kill();
|
||
_returnTween = null;
|
||
}
|
||
if (_spinTween != null)
|
||
{
|
||
_spinTween.Kill();
|
||
_spinTween = null;
|
||
}
|
||
_spinAngleOffset = 0f;
|
||
_isSpinning = false;
|
||
_isReturning = false;
|
||
|
||
_isDragging = false;
|
||
_hasTriggered = false;
|
||
_currentRotation = 0f;
|
||
|
||
if (crankTransform != null)
|
||
{
|
||
ApplyCrankTransform(0f);
|
||
crankTransform.localScale = _initialScale;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 逆时针转一圈的表现(可被 Yarn 命令、事件等触发)
|
||
/// </summary>
|
||
/// <param name="duration">动画时长,不传则使用 spinOnceDuration</param>
|
||
public void PlaySpinCounterClockwise(float? duration = null)
|
||
{
|
||
float d = duration ?? spinOnceDuration;
|
||
if (d <= 0f) d = 0.5f;
|
||
|
||
if (_spinTween != null)
|
||
{
|
||
_spinTween.Kill();
|
||
_spinTween = null;
|
||
}
|
||
|
||
_spinAngleOffset = 0f;
|
||
_isSpinning = true;
|
||
|
||
_spinTween = DOTween.To(
|
||
() => _spinAngleOffset,
|
||
value =>
|
||
{
|
||
_spinAngleOffset = value;
|
||
ApplyCrankTransform(_currentRotation);
|
||
},
|
||
360f,
|
||
d
|
||
).SetEase(Ease.Linear).OnComplete(() =>
|
||
{
|
||
_spinAngleOffset = 0f;
|
||
_spinTween = null;
|
||
_isSpinning = false;
|
||
ApplyCrankTransform(_currentRotation);
|
||
onSpinOnceComplete?.Invoke();
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 手动旋转到指定角度(用于动画或程序化控制)
|
||
/// </summary>
|
||
public void RotateTo(float angle, float duration = 0.5f)
|
||
{
|
||
angle = Mathf.Clamp(angle, 0f, maxRotationAngle);
|
||
|
||
if (_returnTween != null)
|
||
{
|
||
_returnTween.Kill();
|
||
_returnTween = null;
|
||
}
|
||
|
||
DOTween.To(
|
||
() => _currentRotation,
|
||
value => {
|
||
_currentRotation = value;
|
||
ApplyCrankTransform(_currentRotation);
|
||
float normalizedRotation = _currentRotation / maxRotationAngle;
|
||
onCrankRotate?.Invoke(normalizedRotation);
|
||
},
|
||
angle,
|
||
duration
|
||
).SetEase(Ease.OutCubic);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前旋转角度(0-1标准化)
|
||
/// </summary>
|
||
public float GetNormalizedRotation()
|
||
{
|
||
return _currentRotation / maxRotationAngle;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前旋转角度(度)
|
||
/// </summary>
|
||
public float GetCurrentAngle()
|
||
{
|
||
return _currentRotation;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否正在拖拽中
|
||
/// </summary>
|
||
public bool IsDragging => _isDragging;
|
||
|
||
/// <summary>
|
||
/// 是否已达到触发角度
|
||
/// </summary>
|
||
public bool HasTriggered => _hasTriggered;
|
||
|
||
/// <summary>
|
||
/// 是否正在播放逆时针转圈
|
||
/// </summary>
|
||
public bool IsSpinning => _isSpinning;
|
||
|
||
/// <summary>
|
||
/// 回位时长(秒),供滑片等配合使用
|
||
/// </summary>
|
||
public float ReturnDuration => returnDuration;
|
||
|
||
/// <summary>
|
||
/// 设置回位时长(由 SalesManager 同步每次转动时间)
|
||
/// </summary>
|
||
public void SetReturnDuration(float duration)
|
||
{
|
||
returnDuration = Mathf.Max(0.01f, duration);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region IInteraction(EventTriggerEx 统一交互)
|
||
|
||
public bool IsActive => true;
|
||
|
||
public bool IsAvailable => true;
|
||
|
||
public GameObject GetGameObject() => gameObject;
|
||
|
||
#endregion
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发条交互桥接:挂在 Collider 所在子物体上,将 IInteraction 转交给父级 CrankController
|
||
/// </summary>
|
||
public class CrankInteractionBridge : MonoBehaviour, IInteraction
|
||
{
|
||
private CrankController _controller;
|
||
|
||
private void Awake()
|
||
{
|
||
_controller = GetComponentInParent<CrankController>();
|
||
}
|
||
|
||
public bool IsActive => _controller?.IsActive ?? true;
|
||
public bool IsAvailable => _controller?.IsAvailable ?? true;
|
||
public GameObject GetGameObject() => gameObject;
|
||
}
|
||
}
|