refactor(huoshan): SliderController 移除波形联动、修正顺时针逻辑、添加 Gizmo

Made-with: Cursor
This commit is contained in:
2026-02-27 15:04:50 +08:00
parent 64d127f7fb
commit 03b77ef1bf
@@ -6,11 +6,11 @@ using DG.Tweening;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 滑片控制器 - 实现滑片沿圆环滑动以及与波形联动
/// 滑片控制器 - 实现滑片沿圆环滑动
/// 功能:
/// - 仅通过Yarn指令控制移动,不接受玩家直接交互
/// - 滑片角度/弧度驱动波形参数
/// - 等离子体发光段:从起始位置到当前位置显示等离子体进度指示
/// - 位置变化通过 onSliderPositionChanged 通知(EmotionWaveManager 等可订阅)
/// </summary>
public class SliderController : MonoBehaviour
{
@@ -75,17 +75,6 @@ namespace AibisDream.MiniGame.HuoShan
[Tooltip("粒子渲染 Sorting Order")]
[SerializeField] private int plasmaSortingOrder = 51;
[Header("兼容旧版 LineRenderer(可留空)")]
[Tooltip("若存在则禁用,改用 ParticleSystem")]
[SerializeField] private LineRenderer plasmaLineRenderer;
[Header("波形联动")]
[Tooltip("波形检测器引用")]
[SerializeField] private WaveformDetector waveformDetector;
[Tooltip("等离子体发光影响波形参数")]
[SerializeField] private bool plasmaAffectsWaveform = true;
[Header("事件")]
[Tooltip("滑片位置改变时触发(参数:标准化位置0-1)")]
public UnityEvent<float> onSliderPositionChanged;
@@ -103,7 +92,7 @@ namespace AibisDream.MiniGame.HuoShan
public UnityEvent onSlideComplete;
[Header("调试测试")]
[Tooltip("勾选后,拖拽下方滑块可直接测试滑片与弧线(Play 模式下生效);有程序控制滑动时会自动跳过")]
[Tooltip("勾选后,拖拽下方滑块可直接测试滑片与弧线(Play 模式下生效,优先于程序控制)")]
[SerializeField] private bool useTestSlider;
[Tooltip("测试用滑块:0=起始位置,1=终点位置")]
@@ -111,7 +100,6 @@ namespace AibisDream.MiniGame.HuoShan
private float _currentAngle = 0f;
private float _targetAngle = 0f;
private WaveformRenderer _waveformRenderer;
private bool _phaseSemanticFired;
private bool _phaseReviewFired;
private bool _phaseStyleFired;
@@ -132,20 +120,8 @@ namespace AibisDream.MiniGame.HuoShan
sliderTransform = transform;
}
// 禁用旧版 LineRenderer(改用 ParticleSystem
if (plasmaLineRenderer != null)
{
plasmaLineRenderer.enabled = false;
}
// 初始化等离子体粒子系统
EnsurePlasmaParticleSystem();
// 获取波形渲染器
if (waveformDetector != null && waveformDetector.IsShown)
{
_waveformRenderer = waveformDetector.GetComponentInChildren<WaveformRenderer>();
}
}
private void Update()
@@ -166,22 +142,16 @@ namespace AibisDream.MiniGame.HuoShan
#region
private void UpdateSliderPosition()
/// <summary>
/// 根据角度应用滑片位置与旋转(顺时针移动:角度取负)
/// </summary>
/// <returns>滑片世界坐标</returns>
private Vector3 ApplyAngleToTransform(float angle)
{
#if UNITY_EDITOR
// 调试模式:Inspector 滑块直接驱动滑片(有程序控制的滑动时跳过,避免覆盖 crank 驱动)
if (useTestSlider && Application.isPlaying && (_moveTween == null || !_moveTween.IsActive()))
{
_targetAngle = testSliderPosition * slideAngleRange;
}
#endif
if (ringCenter == null || sliderTransform == null) return _lastValidPosition;
// 匀速移动到目标角度(避免 Lerp 导致的越接近越慢、卡顿感)
float maxDelta = slideSpeed * Time.deltaTime;
_currentAngle = Mathf.MoveTowards(_currentAngle, _targetAngle, maxDelta);
// 计算滑片在世界空间的位置
float radian = (_currentAngle + startAngleOffset) * Mathf.Deg2Rad;
// 顺时针:标准数学角度增加为逆时针,取负则为顺时针
float radian = -(angle + startAngleOffset) * Mathf.Deg2Rad;
Vector3 position = ringCenter.position + new Vector3(
Mathf.Cos(radian) * outerRadius,
Mathf.Sin(radian) * outerRadius,
@@ -190,12 +160,39 @@ namespace AibisDream.MiniGame.HuoShan
sliderTransform.position = position;
// 滑片沿轨道顺时针方向(切线方向,与逆时针相反
// 滑片沿轨道顺时针方向(切线方向)
Vector3 direction = position - ringCenter.position;
float radialAngle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
float tangentAngle = radialAngle - 90f; // 顺时针切线
sliderTransform.rotation = Quaternion.Euler(0f, 0f, tangentAngle);
return position;
}
private void UpdateSliderPosition()
{
#if UNITY_EDITOR
// 调试模式:Inspector 滑块直接驱动滑片,勾选后始终优先(覆盖程序控制)
if (useTestSlider && Application.isPlaying)
{
if (_moveTween != null && _moveTween.IsActive())
{
_moveTween.Kill();
_moveTween = null;
}
_targetAngle = testSliderPosition * slideAngleRange;
}
#endif
// 匀速移动到目标角度(避免 Lerp 导致的越接近越慢、卡顿感)
float maxDelta = slideSpeed * Time.deltaTime;
_currentAngle = Mathf.MoveTowards(_currentAngle, _targetAngle, maxDelta);
if (ringCenter == null || sliderTransform == null) return;
// 计算滑片在世界空间的位置(顺时针:角度取负)
Vector3 position = ApplyAngleToTransform(_currentAngle);
// 夹子不跟随滑片移动,保持原位
// 触发位置改变事件
@@ -203,7 +200,6 @@ namespace AibisDream.MiniGame.HuoShan
if (Vector3.Distance(position, _lastValidPosition) > 0.01f)
{
onSliderPositionChanged?.Invoke(normalizedPosition);
UpdateWaveform(normalizedPosition);
_lastValidPosition = position;
// 阶段阈值事件:1/3 语义组织, 2/3 审查, 1 风格
@@ -360,8 +356,9 @@ namespace AibisDream.MiniGame.HuoShan
return;
}
float startRad = startAngleOffset * Mathf.Deg2Rad;
float endRad = (startAngleOffset + _currentAngle) * Mathf.Deg2Rad;
// 顺时针:弧线从 startAngleOffset 向负角度延伸
float startRad = -startAngleOffset * Mathf.Deg2Rad;
float endRad = -(startAngleOffset + _currentAngle) * Mathf.Deg2Rad;
float arcSpan = endRad - startRad;
float z = sliderTransform != null ? sliderTransform.position.z : ringCenter.position.z;
@@ -405,34 +402,90 @@ namespace AibisDream.MiniGame.HuoShan
#endregion
#region
#region Gizmo
private void UpdateWaveform(float normalizedPosition)
private void OnDrawGizmosSelected()
{
if (!plasmaAffectsWaveform || waveformDetector == null) return;
var center = ringCenter != null ? ringCenter.position : transform.position;
float z = sliderTransform != null ? sliderTransform.position.z : center.z;
// 将滑片位置映射到波形参数
float waveformPosition = normalizedPosition;
// 外环
Gizmos.color = new Color(0.2f, 0.8f, 1f, 0.6f);
DrawCircleGizmo(center, outerRadius, z, 64);
// 更新波形检测器的扫描位置
if (waveformDetector.IsScanMode)
// 内环(等离子体弧线)
Gizmos.color = new Color(0.3f, 0.9f, 0.55f, 0.5f);
DrawCircleGizmo(center, innerRadius, z, 64);
// 起始位置标记(绿色)
float startRad = -(0f + startAngleOffset) * Mathf.Deg2Rad;
Vector3 startPos = center + new Vector3(Mathf.Cos(startRad) * outerRadius, Mathf.Sin(startRad) * outerRadius, 0f);
Gizmos.color = Color.green;
Gizmos.DrawWireSphere(startPos, outerRadius * 0.05f);
// 终点位置标记(黄色)
float endRad = -(slideAngleRange + startAngleOffset) * Mathf.Deg2Rad;
Vector3 endPos = center + new Vector3(Mathf.Cos(endRad) * outerRadius, Mathf.Sin(endRad) * outerRadius, 0f);
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(endPos, outerRadius * 0.05f);
// 段分割线
Gizmos.color = new Color(1f, 1f, 0.5f, 0.3f);
float segAngle = slideAngleRange / Mathf.Max(1, segmentCount);
for (int i = 1; i < segmentCount; i++)
{
// 如果处于扫描模式,更新扫描滑块
// 这会是集成的一部分,具体实现需要根据游戏流程调整
float rad = -(i * segAngle + startAngleOffset) * Mathf.Deg2Rad;
Vector3 segPos = center + new Vector3(Mathf.Cos(rad) * outerRadius, Mathf.Sin(rad) * outerRadius, 0f);
Gizmos.DrawLine(center, segPos);
}
// 更新波形渲染器
if (_waveformRenderer != null)
// 当前滑片位置(红色,仅 Play 模式有值)
#if UNITY_EDITOR
if (Application.isPlaying)
#endif
{
// 根据滑片位置调整波形清晰度
float clarity = Mathf.Lerp(0f, 1f, normalizedPosition);
_waveformRenderer.SetClarity(clarity);
// 根据滑片位置调整波形位置
_waveformRenderer.SetClarityPosition(normalizedPosition);
float curRad = -(_currentAngle + startAngleOffset) * Mathf.Deg2Rad;
Vector3 curPos = center + new Vector3(Mathf.Cos(curRad) * outerRadius, Mathf.Sin(curRad) * outerRadius, 0f);
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(curPos, outerRadius * 0.08f);
}
}
private void DrawCircleGizmo(Vector3 center, float radius, float z, int segments)
{
Vector3 prev = center + new Vector3(radius, 0f, 0f);
for (int i = 1; i <= segments; i++)
{
float rad = (float)i / segments * Mathf.PI * 2f;
Vector3 next = center + new Vector3(Mathf.Cos(rad) * radius, Mathf.Sin(rad) * radius, 0f);
Gizmos.DrawLine(prev, next);
prev = next;
}
}
/// <summary>
/// 在编辑器中一键将滑片重置到初始状态
/// </summary>
[ContextMenu("重置到初始状态")]
private void ResetToInitialStateInEditor()
{
_targetAngle = 0f;
_currentAngle = 0f;
ResetPhaseFlags();
if (_moveTween != null && _moveTween.IsActive())
{
_moveTween.Kill();
_moveTween = null;
}
if (ringCenter != null && sliderTransform != null)
ApplyAngleToTransform(0f);
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
if (gameObject.scene.IsValid())
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(gameObject.scene);
#endif
}
#endregion
#region