实现销售系统核心交互组件: - CrankController: 曲柄/旋钮控制 - KnobController: 旋钮交互逻辑 - ProcessorLightController: 处理器灯光控制 - SalesYarnCommand: Yarn 命令集成 - SliderController: 滑片控制器 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
440 lines
14 KiB
C#
440 lines
14 KiB
C#
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
|
||
}
|
||
}
|