Files
aibis-dream/Assets/Scripts/MiniGame/HuoShan/SalesSystem/SliderController.cs
T

493 lines
17 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 AibisDream;
using UnityEngine;
using UnityEngine.Events;
using DG.Tweening;
namespace AibisDream.MiniGame.HuoShan
{
/// <summary>
/// 滑片控制器 - 实现滑片沿圆环滑动以及与波形联动
/// 功能:
/// - 仅通过Yarn指令控制移动,不接受玩家直接交互
/// - 滑片角度/弧度驱动波形参数
/// - 等离子体发光段:从起始位置到当前位置显示等离子体进度指示
/// </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("离散段数量(用于slide_to_segment等Yarn命令)")]
[SerializeField] private int segmentCount = 12;
[Header("等离子体弧线(Particle System")]
[Tooltip("用于等离子体效果的粒子系统,不赋值则自动创建")]
[SerializeField] private ParticleSystem plasmaParticleSystem;
[Tooltip("粒子数量下限(短弧时)")]
[SerializeField] [Min(2)] private int minParticleCount = 20;
[Tooltip("粒子数量上限(满弧时)")]
[SerializeField] [Min(2)] private int maxParticleCount = 48;
[Tooltip("粒子大小")]
[SerializeField] private float particleSize = 0.08f;
[Tooltip("粒子材质(Additive 发光,需手动引用)")]
[SerializeField] private Material plasmaParticleMaterial;
[Tooltip("粒子纹理(圆形中心亮边沿渐隐,不引用则自动生成软圆)")]
[SerializeField] private Texture2D plasmaParticleTexture;
[Tooltip("流动速度(小球沿弧线移动速度,1=每秒跑完一整圈)")]
[SerializeField] private float flowSpeed = 3f;
[Tooltip("发光颜色")]
[SerializeField] private Color glowColor = new Color(0.3f, 0.9f, 0.55f);
[CustomSortingLayer]
[Tooltip("粒子渲染 Sorting Layer")]
[SerializeField] private int plasmaSortingLayerId;
[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;
[Header("调试测试")]
[Tooltip("勾选后,拖拽下方滑块可直接测试滑片与弧线(Play 模式下生效)")]
[SerializeField] private bool useTestSlider;
[Tooltip("测试用滑块:0=起始位置,1=终点位置")]
[SerializeField] [Range(0f, 1f)] private float testSliderPosition;
private float _currentAngle = 0f;
private float _targetAngle = 0f;
private WaveformRenderer _waveformRenderer;
private Tweener _moveTween;
private Vector3 _lastValidPosition;
private ParticleSystem.Particle[] _particles;
private float[] _particlePhases;
private Material _plasmaMaterialInstance;
private Texture2D _generatedSoftCircleTexture;
#region 生命周期
private void Awake()
{
if (sliderTransform == null)
{
sliderTransform = transform;
}
// 禁用旧版 LineRenderer(改用 ParticleSystem
if (plasmaLineRenderer != null)
{
plasmaLineRenderer.enabled = false;
}
// 初始化等离子体粒子系统
EnsurePlasmaParticleSystem();
// 获取波形渲染器
if (waveformDetector != null && waveformDetector.IsShown)
{
_waveformRenderer = waveformDetector.GetComponentInChildren<WaveformRenderer>();
}
}
private void Update()
{
UpdateSliderPosition();
UpdatePlasmaGlow();
}
private void OnDestroy()
{
if (_plasmaMaterialInstance != null)
Destroy(_plasmaMaterialInstance);
if (_generatedSoftCircleTexture != null)
Destroy(_generatedSoftCircleTexture);
}
#endregion
#region 位置更新
private void UpdateSliderPosition()
{
#if UNITY_EDITOR
// 调试模式:Inspector 滑块直接驱动滑片
if (useTestSlider && Application.isPlaying)
{
_targetAngle = testSliderPosition * slideAngleRange;
}
#endif
// 平滑移动到目标角度
_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 = position - ringCenter.position;
float radialAngle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
float tangentAngle = radialAngle - 90f; // 顺时针切线
sliderTransform.rotation = Quaternion.Euler(0f, 0f, tangentAngle);
// 夹子不跟随滑片移动,保持原位
// 触发位置改变事件
float normalizedPosition = _currentAngle / slideAngleRange;
if (Vector3.Distance(position, _lastValidPosition) > 0.01f)
{
onSliderPositionChanged?.Invoke(normalizedPosition);
UpdateWaveform(normalizedPosition);
_lastValidPosition = position;
}
}
#endregion
#region 等离子体弧线
private void EnsurePlasmaParticleSystem()
{
if (plasmaParticleSystem != null)
{
ConfigureParticleSystem(plasmaParticleSystem);
return;
}
// 自动创建粒子系统
var go = new GameObject("PlasmaParticles");
go.transform.SetParent(transform);
go.transform.localPosition = Vector3.zero;
go.transform.localScale = Vector3.one;
plasmaParticleSystem = go.AddComponent<ParticleSystem>();
ConfigureParticleSystem(plasmaParticleSystem);
var renderer = go.GetComponent<ParticleSystemRenderer>();
if (renderer != null)
renderer.renderMode = ParticleSystemRenderMode.Billboard;
}
private void ConfigureParticleSystem(ParticleSystem ps)
{
var main = ps.main;
main.simulationSpace = ParticleSystemSimulationSpace.World;
main.loop = false;
main.startLifetime = 999f;
main.startSize = particleSize;
main.maxParticles = maxParticleCount;
main.playOnAwake = false;
main.startSpeed = 0f;
main.startColor = glowColor;
var emission = ps.emission;
emission.enabled = false;
var velocity = ps.velocityOverLifetime;
velocity.enabled = false;
var colorOverLifetime = ps.colorOverLifetime;
colorOverLifetime.enabled = false;
var sizeOverLifetime = ps.sizeOverLifetime;
sizeOverLifetime.enabled = false;
var renderer = ps.GetComponent<ParticleSystemRenderer>();
if (renderer != null)
{
ApplyPlasmaMaterial(renderer);
int layerId = SortingLayer.IsValid(plasmaSortingLayerId) ? plasmaSortingLayerId : SortingLayer.layers[0].id;
renderer.sortingLayerID = layerId;
renderer.sortingOrder = plasmaSortingOrder;
}
// 初始化粒子数组
_particles = new ParticleSystem.Particle[maxParticleCount];
_particlePhases = new float[maxParticleCount];
for (int i = 0; i < maxParticleCount; i++)
{
_particlePhases[i] = (float)i / maxParticleCount;
}
ps.Play();
ps.Emit(maxParticleCount);
}
private void ApplyPlasmaMaterial(ParticleSystemRenderer renderer)
{
var tex = plasmaParticleTexture != null ? plasmaParticleTexture : GetOrCreateSoftCircleTexture();
if (tex == null) return;
Material mat = plasmaParticleMaterial != null ? plasmaParticleMaterial : null;
if (mat == null)
{
var shader = Shader.Find("Legacy Shaders/Particles/Additive") ?? Shader.Find("Particles/Additive");
if (shader == null) return;
mat = new Material(shader);
mat.SetColor("_TintColor", Color.white);
}
else
{
mat = new Material(plasmaParticleMaterial);
}
mat.mainTexture = tex;
_plasmaMaterialInstance = mat;
renderer.material = mat;
}
private Texture2D GetOrCreateSoftCircleTexture()
{
if (_generatedSoftCircleTexture != null) return _generatedSoftCircleTexture;
const int size = 64;
_generatedSoftCircleTexture = new Texture2D(size, size);
_generatedSoftCircleTexture.wrapMode = TextureWrapMode.Clamp;
_generatedSoftCircleTexture.filterMode = FilterMode.Bilinear;
var pixels = new Color[size * size];
float center = (size - 1) * 0.5f;
float radius = center;
for (int y = 0; y < size; y++)
for (int x = 0; x < size; x++)
{
float dx = x - center;
float dy = y - center;
float dist = Mathf.Sqrt(dx * dx + dy * dy);
float t = 1f - Mathf.Clamp01(dist / radius);
float alpha = t * t;
pixels[y * size + x] = new Color(1f, 1f, 1f, alpha);
}
_generatedSoftCircleTexture.SetPixels(pixels);
_generatedSoftCircleTexture.Apply();
return _generatedSoftCircleTexture;
}
private void UpdatePlasmaGlow()
{
if (plasmaParticleSystem == null || ringCenter == null || _particles == null) return;
float progress = Mathf.Clamp01(_currentAngle / slideAngleRange);
// 进度为 0 时隐藏粒子
if (progress <= 0.001f)
{
plasmaParticleSystem.SetParticles(_particles, 0);
return;
}
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;
Vector3 center = ringCenter.position;
int aliveCount = plasmaParticleSystem.GetParticles(_particles);
if (aliveCount == 0)
{
plasmaParticleSystem.Emit(maxParticleCount);
return;
}
// 随进度在 min~max 间平滑过渡粒子数量
float countF = Mathf.Lerp(minParticleCount, maxParticleCount, progress);
int actualCount = Mathf.Clamp(Mathf.RoundToInt(countF), minParticleCount, maxParticleCount);
for (int i = 0; i < maxParticleCount; i++)
{
// 相位沿弧线流动
_particlePhases[i] += flowSpeed * Time.deltaTime;
if (_particlePhases[i] >= 1f) _particlePhases[i] -= 1f;
if (_particlePhases[i] < 0f) _particlePhases[i] += 1f;
float t = _particlePhases[i];
float angle = startRad + arcSpan * t;
Vector3 pos = center + new Vector3(Mathf.Cos(angle) * innerRadius, Mathf.Sin(angle) * innerRadius, z);
_particles[i].position = pos;
_particles[i].remainingLifetime = 999f;
_particles[i].startLifetime = 999f;
_particles[i].startSize = particleSize;
_particles[i].startColor = Color.Lerp(
new Color(glowColor.r, glowColor.g, glowColor.b, 0.5f),
glowColor,
t
);
}
plasmaParticleSystem.SetParticles(_particles, actualCount);
}
#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 void ResetPosition()
{
if (_moveTween != null)
{
_moveTween.Kill();
_moveTween = null;
}
_targetAngle = 0f;
_currentAngle = 0f;
}
/// <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
}
}