using UnityEngine; using UnityEngine.Events; using DG.Tweening; namespace AibisDream.MiniGame.HuoShan { /// /// 滑片控制器 - 实现滑片沿圆环滑动以及与波形联动 /// 功能: /// - 滑片拖拽沿外环滑动 /// - 滑片角度/弧度驱动波形参数 /// - 等离子体发光段显示 /// public class SliderController : MonoBehaviour { [Header("圆环配置")] [Tooltip("圆环中心Transform")] [SerializeField] private Transform ringCenter; [Tooltip("外环半径")] [SerializeField] private float outerRadius = 2f; [Tooltip("内环半径(等离子体发光段)")] [SerializeField] private float innerRadius = 1.5f; [Tooltip("滑片滑动弧度范围(度)")] [SerializeField] private float slideAngleRange = 360f; [Tooltip("滑片起始角度偏移(度)")] [SerializeField] private float startAngleOffset = 0f; [Header("滑片配置")] [Tooltip("滑片Transform")] [SerializeField] private Transform sliderTransform; [Tooltip("滑片电夹Transform")] [SerializeField] private Transform clampTransform; [Tooltip("滑片拖拽阻力")] [SerializeField] private float dragResistance = 0.05f; [Tooltip("是否自动吸附到离散位置")] [SerializeField] private bool snapToSegments = true; [Tooltip("离散段数量")] [SerializeField] private int segmentCount = 12; [Header("等离子体发光")] [Tooltip("等离子体发光对象(可多个)")] [SerializeField] private GameObject[] plasmaObjects; [Tooltip("等离子体发光材质属性名")] [SerializeField] private string glowProperty = "_GlowIntensity"; [Tooltip("发光强度")] [SerializeField] private float glowIntensity = 1f; [Tooltip("发光颜色")] [SerializeField] private Color glowColor = new Color(0.3f, 0.9f, 0.55f); [Header("波形联动")] [Tooltip("波形检测器引用")] [SerializeField] private WaveformDetector waveformDetector; [Tooltip("等离子体发光影响波形参数")] [SerializeField] private bool plasmaAffectsWaveform = true; [Header("事件")] [Tooltip("滑片位置改变时触发(参数:标准化位置0-1)")] public UnityEvent onSliderPositionChanged; [Tooltip("滑片开始拖拽时触发")] public UnityEvent onSliderStartDrag; [Tooltip("滑片释放时触发")] public UnityEvent onSliderReleased; private bool _isDragging = false; private float _currentAngle = 0f; private float _targetAngle = 0f; private float _startAngle = 0f; private Camera _mainCamera; private Vector3 _dragStartPosition; private Material[] _plasmaMaterials; private int _glowPropertyID; private WaveformRenderer _waveformRenderer; private Tweener _moveTween; private Vector3 _lastValidPosition; #region 生命周期 private void Awake() { _mainCamera = Camera.main; _glowPropertyID = Shader.PropertyToID(glowProperty); if (sliderTransform == null) { sliderTransform = transform; } // 缓存等离子体材质 if (plasmaObjects != null && plasmaObjects.Length > 0) { _plasmaMaterials = new Material[plasmaObjects.Length]; for (int i = 0; i < plasmaObjects.Length; i++) { if (plasmaObjects[i] != null) { Renderer renderer = plasmaObjects[i].GetComponent(); if (renderer != null) { _plasmaMaterials[i] = renderer.material; } } } } // 获取波形渲染器 if (waveformDetector != null && waveformDetector.IsShown) { _waveformRenderer = waveformDetector.GetComponentInChildren(); } } private void Update() { HandleMouseInput(); UpdateSliderPosition(); UpdatePlasmaGlow(); } #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; _startAngle = GetMouseAngle(); if (_moveTween != null) { _moveTween.Kill(); _moveTween = null; } onSliderStartDrag?.Invoke(); } private void ContinueDrag() { // 计算鼠标相对于中心的角度 float mouseAngle = GetMouseAngle(); float deltaAngle = mouseAngle - _startAngle; // 限制角度范围 deltaAngle = Mathf.Clamp(deltaAngle, 0f, slideAngleRange); // 应用阻力 _targetAngle = deltaAngle * (1f - dragResistance); // 更新起始角度用于下次计算 _startAngle = mouseAngle; } private void EndDrag() { _isDragging = false; // 如果启用了离散位置,吸附到最近的段 if (snapToSegments) { SnapToNearestSegment(); } // 触发释放事件 float normalizedPosition = _currentAngle / slideAngleRange; onSliderReleased?.Invoke(normalizedPosition); } private float GetMouseAngle() { Vector3 mouseWorldPos = _mainCamera.ScreenToWorldPoint(Input.mousePosition); Vector3 direction = mouseWorldPos - ringCenter.position; float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; if (angle < 0) angle += 360f; return angle; } private void SnapToNearestSegment() { float segmentAngle = slideAngleRange / segmentCount; int targetSegment = Mathf.RoundToInt(_currentAngle / segmentAngle); _targetAngle = targetSegment * segmentAngle; } #endregion #region 位置更新 private void UpdateSliderPosition() { // 平滑移动到目标角度 _currentAngle = Mathf.Lerp(_currentAngle, _targetAngle, Time.deltaTime * 10f); // 计算滑片在世界空间的位置 float radian = (_currentAngle + startAngleOffset) * Mathf.Deg2Rad; Vector3 position = ringCenter.position + new Vector3( Mathf.Cos(radian) * outerRadius, Mathf.Sin(radian) * outerRadius, sliderTransform.position.z ); sliderTransform.position = position; // 滑片朝向中心 Vector3 direction = ringCenter.position - position; float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; sliderTransform.rotation = Quaternion.Euler(0f, 0f, angle); // 更新电夹位置(保持在圆环上) if (clampTransform != null) { Vector3 clampPosition = ringCenter.position + new Vector3( Mathf.Cos(radian) * (outerRadius - 0.2f), Mathf.Sin(radian) * (outerRadius - 0.2f), clampTransform.position.z ); clampTransform.position = clampPosition; } // 触发位置改变事件 float normalizedPosition = _currentAngle / slideAngleRange; if (Vector3.Distance(position, _lastValidPosition) > 0.01f) { onSliderPositionChanged?.Invoke(normalizedPosition); UpdateWaveform(normalizedPosition); _lastValidPosition = position; } } #endregion #region 等离子体发光 private void UpdatePlasmaGlow() { if (_plasmaMaterials == null || _plasmaMaterials.Length == 0) return; // 计算滑片当前位置占整个圆环的比例 float progress = _currentAngle / slideAngleRange; float glowRange = progress * glowIntensity; // 更新每个等离子体对象的发光强度 for (int i = 0; i < _plasmaMaterials.Length; i++) { if (_plasmaMaterials[i] != null) { // 根据等离子体对象的索引计算其位置 float plasmaProgress = (float)i / _plasmaMaterials.Length; // 只有在滑片经过的位置才发光 if (plasmaProgress <= progress) { float localGlow = Mathf.Lerp(0f, glowIntensity, 1f - (progress - plasmaProgress)); _plasmaMaterials[i].SetFloat(_glowPropertyID, localGlow); _plasmaMaterials[i].SetColor("_Color", glowColor); } else { _plasmaMaterials[i].SetFloat(_glowPropertyID, 0f); } } } } #endregion #region 波形联动 private void UpdateWaveform(float normalizedPosition) { if (!plasmaAffectsWaveform || waveformDetector == null) return; // 将滑片位置映射到波形参数 float waveformPosition = normalizedPosition; // 更新波形检测器的扫描位置 if (waveformDetector.IsScanMode) { // 如果处于扫描模式,更新扫描滑块 // 这会是集成的一部分,具体实现需要根据游戏流程调整 } // 更新波形渲染器 if (_waveformRenderer != null) { // 根据滑片位置调整波形清晰度 float clarity = Mathf.Lerp(0f, 1f, normalizedPosition); _waveformRenderer.SetClarity(clarity); // 根据滑片位置调整波形位置 _waveformRenderer.SetClarityPosition(normalizedPosition); } } #endregion #region 公共方法 /// /// 移动到指定角度 /// public void SlideTo(float angle, float duration = 0.5f) { angle = Mathf.Clamp(angle, 0f, slideAngleRange); if (_moveTween != null) { _moveTween.Kill(); } _moveTween = DOTween.To( () => _targetAngle, value => _targetAngle = value, angle, duration ).SetEase(Ease.OutCubic); } /// /// 移动到指定位置(0-1标准化) /// public void SlideToNormalized(float normalizedPosition, float duration = 0.5f) { float angle = normalizedPosition * slideAngleRange; SlideTo(angle, duration); } /// /// 移动到指定段 /// public void SlideToSegment(int segmentIndex, float duration = 0.5f) { if (segmentIndex < 0 || segmentIndex > segmentCount) return; float segmentAngle = slideAngleRange / segmentCount * segmentIndex; SlideTo(segmentAngle, duration); } /// /// 获取当前角度 /// public float GetCurrentAngle() { return _currentAngle; } /// /// 获取标准化位置(0-1) /// public float GetNormalizedPosition() { return _currentAngle / slideAngleRange; } /// /// 是否正在拖拽 /// public bool IsDragging => _isDragging; /// /// 重置滑片位置 /// public void ResetPosition() { if (_moveTween != null) { _moveTween.Kill(); _moveTween = null; } _targetAngle = 0f; _currentAngle = 0f; _isDragging = false; } /// /// 设置离散段数量 /// public void SetSegmentCount(int count) { segmentCount = Mathf.Max(1, count); } /// /// 获取当前段索引 /// public int GetCurrentSegment() { float segmentAngle = slideAngleRange / segmentCount; return Mathf.FloorToInt(_currentAngle / segmentAngle); } #endregion } }