Files
aibis-dream/Assets/Scripts/MiniGame/HuoShan/SalesSystem/SliderController.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

440 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEngine.Events;
using DG.Tweening;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 滑片控制器 - 实现滑片沿圆环滑动以及与波形联动
/// 功能:
/// - 滑片拖拽沿外环滑动
/// - 滑片角度/弧度驱动波形参数
/// - 等离子体发光段显示
/// </summary>
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<float> onSliderPositionChanged;
[Tooltip("滑片开始拖拽时触发")]
public UnityEvent onSliderStartDrag;
[Tooltip("滑片释放时触发")]
public UnityEvent<float> 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<Renderer>();
if (renderer != null)
{
_plasmaMaterials[i] = renderer.material;
}
}
}
}
// 获取波形渲染器
if (waveformDetector != null && waveformDetector.IsShown)
{
_waveformRenderer = waveformDetector.GetComponentInChildren<WaveformRenderer>();
}
}
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 公共方法
/// <summary>
/// 移动到指定角度
/// </summary>
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);
}
/// <summary>
/// 移动到指定位置(0-1标准化)
/// </summary>
public void SlideToNormalized(float normalizedPosition, float duration = 0.5f)
{
float angle = normalizedPosition * slideAngleRange;
SlideTo(angle, duration);
}
/// <summary>
/// 移动到指定段
/// </summary>
public void SlideToSegment(int segmentIndex, float duration = 0.5f)
{
if (segmentIndex < 0 || segmentIndex > segmentCount) return;
float segmentAngle = slideAngleRange / segmentCount * segmentIndex;
SlideTo(segmentAngle, duration);
}
/// <summary>
/// 获取当前角度
/// </summary>
public float GetCurrentAngle()
{
return _currentAngle;
}
/// <summary>
/// 获取标准化位置(0-1
/// </summary>
public float GetNormalizedPosition()
{
return _currentAngle / slideAngleRange;
}
/// <summary>
/// 是否正在拖拽
/// </summary>
public bool IsDragging => _isDragging;
/// <summary>
/// 重置滑片位置
/// </summary>
public void ResetPosition()
{
if (_moveTween != null)
{
_moveTween.Kill();
_moveTween = null;
}
_targetAngle = 0f;
_currentAngle = 0f;
_isDragging = false;
}
/// <summary>
/// 设置离散段数量
/// </summary>
public void SetSegmentCount(int count)
{
segmentCount = Mathf.Max(1, count);
}
/// <summary>
/// 获取当前段索引
/// </summary>
public int GetCurrentSegment()
{
float segmentAngle = slideAngleRange / segmentCount;
return Mathf.FloorToInt(_currentAngle / segmentAngle);
}
#endregion
}
}