Files
aibis-dream/Assets/Scripts/MiniGame/HuoShan/SalesSystem/CrankController.cs
T
bottlefishandClaude Opus 4.6 99f972820e feat(系统): 新增销售系统交互控制器
实现销售系统核心交互组件:
- CrankController: 曲柄/旋钮控制
- KnobController: 旋钮交互逻辑
- ProcessorLightController: 处理器灯光控制
- SalesYarnCommand: Yarn 命令集成
- SliderController: 滑片控制器

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:48:44 +08:00

297 lines
8.8 KiB
C#

using UnityEngine;
using UnityEngine.Events;
using DG.Tweening;
using System.Collections;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 发条控制器 - 实现发条的拖拽旋转交互
/// 功能:拖拽旋转,最大260°,缓慢回位,触发事件
/// </summary>
public class CrankController : MonoBehaviour
{
[Header("发条配置")]
[Tooltip("最大旋转角度(度)")]
[SerializeField] private float maxRotationAngle = 260f;
[Tooltip("回位速度(度/秒)")]
[SerializeField] private float returnSpeed = 45f;
[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("发条旋转的中心Transform")]
[SerializeField] private Transform crankTransform;
[Tooltip("拖拽时的缩放效果")]
[SerializeField] private float dragScale = 1.05f;
[Tooltip("缩放动画时长")]
[SerializeField] private float scaleDuration = 0.1f;
private Vector3 _dragStartPosition;
private float _currentRotation = 0f;
private bool _isDragging = false;
private bool _hasTriggered = false;
private Camera _mainCamera;
private Vector3 _initialScale;
private Tweener _scaleTween;
private Tweener _returnTween;
#region 生命周期
private void Awake()
{
_mainCamera = Camera.main;
if (crankTransform != null)
{
_initialScale = crankTransform.localScale;
}
else
{
_initialScale = transform.localScale;
crankTransform = transform;
}
}
private void OnEnable()
{
ResetCrank();
}
private void Update()
{
HandleMouseInput();
HandleReturn();
}
#endregion
#region 输入处理
private void HandleMouseInput()
{
if (Input.GetMouseButtonDown(0))
{
Ray ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit2D hit = Physics2D.Raycast(ray.origin, ray.direction);
if (hit.collider != null && hit.collider.gameObject == gameObject)
{
StartDrag();
}
}
if (_isDragging)
{
if (Input.GetMouseButton(0))
{
ContinueDrag();
}
else if (Input.GetMouseButtonUp(0))
{
EndDrag();
}
}
}
#endregion
#region 拖拽逻辑
private void StartDrag()
{
_isDragging = true;
_dragStartPosition = Input.mousePosition;
_hasTriggered = false;
if (_returnTween != null)
{
_returnTween.Kill();
_returnTween = null;
}
// 拖拽缩放效果
if (_scaleTween != null) _scaleTween.Kill();
_scaleTween = crankTransform.DOScale(_initialScale * dragScale, scaleDuration)
.SetEase(Ease.OutQuad);
onCrankStartDrag?.Invoke();
}
private void ContinueDrag()
{
// 获取鼠标相对于中心的角度
Vector3 screenCenter = _mainCamera.WorldToScreenPoint(crankTransform.position);
Vector3 currentMousePos = Input.mousePosition;
// 计算角度
float angle = Mathf.Atan2(currentMousePos.y - screenCenter.y, currentMousePos.x - screenCenter.x) * Mathf.Rad2Deg;
// 将角度转换为0-360范围
if (angle < 0) angle += 360f;
// 限制最大旋转角度
angle = Mathf.Clamp(angle, 0f, maxRotationAngle);
// 应用阻力效果
float targetRotation = angle * (1f - dragResistance);
_currentRotation = Mathf.Lerp(_currentRotation, targetRotation, Time.deltaTime * 10f);
// 更新旋转
crankTransform.localRotation = Quaternion.Euler(0f, 0f, -_currentRotation);
// 触发旋转事件
float normalizedRotation = _currentRotation / maxRotationAngle;
onCrankRotate?.Invoke(normalizedRotation);
// 检查是否达到触发角度
if (_currentRotation >= triggerAngle && !_hasTriggered)
{
_hasTriggered = true;
// 触发完成事件将在释放时调用
}
}
private void EndDrag()
{
_isDragging = false;
// 恢复缩放
if (_scaleTween != null) _scaleTween.Kill();
_scaleTween = crankTransform.DOScale(_initialScale, scaleDuration)
.SetEase(Ease.OutQuad);
// 触发完成事件
if (_hasTriggered && _currentRotation >= triggerAngle)
{
onCrankComplete?.Invoke();
}
}
#endregion
#region 回位逻辑
private void HandleReturn()
{
if (!_isDragging && autoReturn && _currentRotation > 0f)
{
if (_returnTween == null)
{
float returnDuration = _currentRotation / returnSpeed;
_returnTween = DOTween.To(
() => _currentRotation,
value => {
_currentRotation = value;
crankTransform.localRotation = Quaternion.Euler(0f, 0f, -_currentRotation);
float normalizedRotation = _currentRotation / maxRotationAngle;
onCrankRotate?.Invoke(normalizedRotation);
},
0f,
returnDuration
).SetEase(Ease.OutCubic).OnComplete(() => {
_currentRotation = 0f;
_returnTween = null;
});
}
}
}
#endregion
#region 公共方法
public void ResetCrank()
{
if (_returnTween != null)
{
_returnTween.Kill();
_returnTween = null;
}
_isDragging = false;
_hasTriggered = false;
_currentRotation = 0f;
if (crankTransform != null)
{
crankTransform.localRotation = Quaternion.identity;
crankTransform.localScale = _initialScale;
}
}
/// <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;
crankTransform.localRotation = Quaternion.Euler(0f, 0f, -_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;
#endregion
}
}