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
}
}