using System.Collections; using UnityEngine; using UnityEngine.UI; using DG.Tweening; using AibisDream.FixSystem; namespace AibisDream.MiniGame.HuoShan { /// /// 波形检测器 - 用于显示和分析波形 /// /// 功能: /// - 显示各种波形(情绪、逻辑、笑话等) /// - 扫描模式:拖动进度条寻找清晰波形 /// - 波形效果:检测闪烁、变换动画等 /// public class WaveformDetector : MonoBehaviour { #region 序列化字段 [Header("设备配置")] [SerializeField] private WaveformConfig waveformConfig; [SerializeField] private Transform deviceContainer; [Header("波形渲染器")] [SerializeField] private WaveformRenderer primaryWaveform; [Header("动画设置")] [SerializeField] private Vector2 hiddenPosition = new Vector2(800f, 0f); [SerializeField] private Vector2 shownPosition = new Vector2(0f, 0f); [SerializeField] private float slideInDuration = 0.5f; [SerializeField] private float slideOutDuration = 0.3f; // 复古机械感:避免OutBack/InBack这种“弹簧感”过强的曲线 [SerializeField] private Ease slideInEase = Ease.OutCubic; [SerializeField] private Ease slideOutEase = Ease.InCubic; [Header("扫描UI")] [SerializeField] private GameObject scannerPanel; [SerializeField] private Slider scanSlider; [SerializeField] private RawImage visitedAreaImage; [SerializeField] private Text scanStatusText; [SerializeField] private Button lockButton; [SerializeField] private Text lockButtonText; [Header("扫描设置")] [SerializeField] private float clearPointPosition = 0.35f; [SerializeField] private float clearThreshold = 0.15f; [SerializeField] private float visitedCoverageRequired = 0.9f; [SerializeField] private float notFoundDelay = 1f; [Header("扫描表现")] [SerializeField] private float scanBlurClarity = 0.06f; [SerializeField] private float scanBlurNoise = 0.75f; [SerializeField] private float scanClearNoise = 0.08f; [SerializeField] private float scanClarityWidth = 0.06f; [SerializeField] private float scanClarityRangeMultiplier = 2.0f; [SerializeField] private float scanClarityPower = 2.2f; [SerializeField] private float scanLogicSwitchClarity = 0.7f; [SerializeField] private Vector2 scanFilterLogicWeightRange = new Vector2(0.35f, 0.6f); [SerializeField] private Vector2 scanFilterJokeWeightRange = new Vector2(0.7f, 0.3f); [Header("结果UI")] [SerializeField] private GameObject resultPanel; [SerializeField] private Text resultText; [Header("连接状态")] [SerializeField] private Text connectionStatusText; [SerializeField] private Image connectionIndicator; [Header("复古机械风格(UI/提示色)")] [SerializeField] private bool useMechanicalUiStyle = true; [SerializeField] private Color uiOnlineColor = new Color(0.30f, 0.90f, 0.55f, 1f); // CRT绿 [SerializeField] private Color uiOfflineColor = new Color(0.40f, 0.40f, 0.40f, 1f); [SerializeField] private Color uiWarnColor = new Color(1.00f, 0.70f, 0.20f, 1f); // 仪表琥珀 [SerializeField] private Color uiErrorColor = new Color(0.95f, 0.30f, 0.25f, 1f); [SerializeField] private Color visitedColor = new Color(0.30f, 0.90f, 0.55f, 0.65f); [SerializeField] private Color unvisitedColor = new Color(0.18f, 0.18f, 0.18f, 0.35f); [SerializeField] private Color sweepScanLineColor = new Color(1.00f, 0.70f, 0.20f, 0.85f); [Header("复古机械风格(文案)")] [SerializeField] private string statusPrefix = "状态:"; [SerializeField] private string scanCalibratingText = "校准中:调节游标,寻找稳定窗口…"; [SerializeField] private string scanSearchingText = "扫描中:寻找稳定窗口…"; [SerializeField] private string scanFineTuneText = "信号趋稳:继续微调…"; [SerializeField] private string scanReadyToLockText = "信号稳定:允许锁定"; [SerializeField] private string scanCompletedNotFoundText = "扫描完成:未检出匹配波形"; [SerializeField] private string emotionNotFoundText = "扫描完成:输出端未检出原始情绪波形"; [Header("音效")] [SerializeField] private AudioClip scanSound; [SerializeField] private AudioClip lockSound; [SerializeField] private AudioClip notFoundSound; [SerializeField] private AudioClip connectSound; [SerializeField] private AudioClip transformSound; #endregion #region 私有字段 private bool _isShown = false; private AudioSource _audioSource; // 扫描模式 private bool _isScanMode = false; private ScanMode _currentScanMode = ScanMode.None; private bool _isLocked = false; private float _lockedPosition = 0f; private string _lockedTimestamp = ""; // 已访问区域追踪 private bool[] _visitedSegments; private int _segmentCount = 100; private float _visitedCoverage = 0f; private bool _fullyCovered = false; private float _notFoundTimer = 0f; private bool _notFoundTriggered = false; private Texture2D _visitedTexture; // 当前连接的模块 private string _connectedModuleId = ""; #endregion #region 枚举 public enum ScanMode { None, Logic, Emotion } #endregion #region 属性 public bool IsShown => _isShown; public bool IsScanMode => _isScanMode; public bool IsLocked => _isLocked; public bool FullyCovered => _fullyCovered; public bool NotFoundTriggered => _notFoundTriggered; public string LockedTimestamp => _lockedTimestamp; public string ConnectedModuleId => _connectedModuleId; #endregion #region 生命周期 private void Awake() { _audioSource = GetComponent(); if (_audioSource == null) { _audioSource = gameObject.AddComponent(); } if (deviceContainer != null) { deviceContainer.localPosition = hiddenPosition; } HideAllPanels(); _visitedSegments = new bool[_segmentCount]; CreateVisitedTexture(); if (scanSlider != null) { scanSlider.onValueChanged.AddListener(OnScanSliderChanged); } if (lockButton != null) { lockButton.onClick.AddListener(OnLockButtonClicked); } FixSystemCenter.SystemDic.Register(this); } private void OnDestroy() { if (scanSlider != null) { scanSlider.onValueChanged.RemoveListener(OnScanSliderChanged); } if (lockButton != null) { lockButton.onClick.RemoveListener(OnLockButtonClicked); } if (_visitedTexture != null) { Destroy(_visitedTexture); } FixSystemCenter.SystemDic.Unregister(); } private void Update() { if (_isScanMode && _currentScanMode == ScanMode.Emotion && _fullyCovered && !_notFoundTriggered) { _notFoundTimer += Time.deltaTime; if (_notFoundTimer >= notFoundDelay) { TriggerNotFound(); } } } private void HideAllPanels() { if (scannerPanel != null) scannerPanel.SetActive(false); if (resultPanel != null) resultPanel.SetActive(false); } #endregion #region 设备显示/隐藏 public IEnumerator Show(float duration = -1f) { if (_isShown) yield break; float actualDuration = duration > 0 ? duration : slideInDuration; _isShown = true; if (deviceContainer != null) { // 这里使用localPosition初始化,因此也使用DOLocalMove,避免世界坐标导致的“漂移感” yield return deviceContainer.DOMove(shownPosition, actualDuration) .SetEase(slideInEase) .WaitForCompletion(); } } public IEnumerator Hide(float duration = -1f) { if (!_isShown) yield break; float actualDuration = duration > 0 ? duration : slideOutDuration; _isShown = false; StopScanMode(); if (deviceContainer != null) { yield return deviceContainer.DOMove(hiddenPosition, actualDuration) .SetEase(slideOutEase) .WaitForCompletion(); } if (primaryWaveform != null) primaryWaveform.SetVisible(false); HideAllPanels(); } #endregion #region 模块连接 public IEnumerator ConnectToModule(string moduleId) { if (!_isShown) { yield return Show(); } _connectedModuleId = moduleId; PlaySound(connectSound); if (connectionStatusText != null) { connectionStatusText.text = useMechanicalUiStyle ? $"LINK: ONLINE / {moduleId}" : $"连接状态: 已联机 / {moduleId}"; } if (connectionIndicator != null) { connectionIndicator.color = useMechanicalUiStyle ? uiOnlineColor : new Color(0.35f, 0.85f, 0.55f); } if (primaryWaveform != null) { var settings = waveformConfig.GetWaveformById("filter_output"); primaryWaveform.ApplySettings(settings); primaryWaveform.SetNoiseAmount(0.8f); primaryWaveform.SetClarity(0f); primaryWaveform.SetVisible(true); } yield return new WaitForSeconds(0.3f); } public void Disconnect() { _connectedModuleId = ""; if (connectionStatusText != null) { connectionStatusText.text = useMechanicalUiStyle ? "LINK: OFFLINE" : "连接状态: 断开"; } if (connectionIndicator != null) { connectionIndicator.color = useMechanicalUiStyle ? uiOfflineColor : new Color(0.45f, 0.45f, 0.45f); } StopScanMode(); } #endregion #region 波形显示 public IEnumerator ShowWaveform(string waveformId, float duration = 0.5f) { if (waveformConfig == null) { Debug.LogError("WaveformDetector: 波形配置未设置!"); yield break; } if (!_isShown) { yield return Show(); } var settings = waveformConfig.GetWaveformById(waveformId); if (primaryWaveform != null) { primaryWaveform.ApplySettings(settings); primaryWaveform.SetClarity(1f); primaryWaveform.SetNoiseAmount(0f); primaryWaveform.SetVisible(true); primaryWaveform.SetAlpha(0f); float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; primaryWaveform.SetAlpha(elapsed / duration); yield return null; } primaryWaveform.SetAlpha(1f); } } public IEnumerator HideWaveform(float duration = 0.3f) { if (primaryWaveform != null && primaryWaveform.gameObject.activeInHierarchy) { float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; primaryWaveform.SetAlpha(1f - elapsed / duration); yield return null; } primaryWaveform.SetVisible(false); } } #endregion #region 扫描模式 public IEnumerator EnableScanMode(ScanMode mode, string label = "扫描波形") { if (!_isShown) { yield return Show(); } _isScanMode = true; _currentScanMode = mode; _isLocked = false; _notFoundTriggered = false; _notFoundTimer = 0f; ResetVisitedArea(); if (mode == ScanMode.Logic) { clearPointPosition = Random.Range(0.25f, 0.45f); } else { clearPointPosition = -1f; } if (primaryWaveform != null && string.IsNullOrEmpty(_connectedModuleId)) { yield return ConnectToModule("滤波器"); } if (scannerPanel != null) { scannerPanel.SetActive(true); } if (scanSlider != null) { scanSlider.value = 0.5f; scanSlider.interactable = true; } if (lockButton != null) { lockButton.interactable = false; if (lockButtonText != null) { lockButtonText.text = "锁定"; } } if (scanStatusText != null) { scanStatusText.text = useMechanicalUiStyle ? $"{scanCalibratingText}({label})" : $"校准中:移动游标{label}..."; } UpdateWaveformByPosition(0.5f); } private void OnScanSliderChanged(float value) { if (!_isScanMode || _isLocked) return; MarkVisited(value); UpdateVisitedAreaDisplay(); UpdateWaveformByPosition(value); bool isClear = IsNearClearPoint(value); if (lockButton != null) { lockButton.interactable = isClear && _currentScanMode == ScanMode.Logic; } UpdateScanStatus(value, isClear); } private void UpdateWaveformByPosition(float position) { if (primaryWaveform == null) return; if (_currentScanMode == ScanMode.Logic) { float distanceFromClear = Mathf.Abs(position - clearPointPosition); float maxDistance = clearThreshold * scanClarityRangeMultiplier; float t = 1f - Mathf.Clamp01(distanceFromClear / maxDistance); float shaped = Mathf.Pow(t, scanClarityPower); float clarity = Mathf.Lerp(scanBlurClarity, 1f, shaped); float noiseAmount = Mathf.Lerp(scanBlurNoise, scanClearNoise, shaped); primaryWaveform.SetClarityPosition(position); primaryWaveform.SetClarityWidth(scanClarityWidth); primaryWaveform.SetClarity(clarity); primaryWaveform.SetNoiseAmount(noiseAmount); if (clarity >= scanLogicSwitchClarity) { primaryWaveform.SetWaveType(WaveformType.Logic); } else { primaryWaveform.SetWaveType(WaveformType.FilterOutput); float logicWeight = Mathf.Lerp(scanFilterLogicWeightRange.x, scanFilterLogicWeightRange.y, shaped); float jokeWeight = Mathf.Lerp(scanFilterJokeWeightRange.x, scanFilterJokeWeightRange.y, shaped); primaryWaveform.SetSynthesisWeights(logicWeight, jokeWeight); } } else { primaryWaveform.SetClarityPosition(position); primaryWaveform.SetClarity(0f); primaryWaveform.SetNoiseAmount(0.8f); } } private bool IsNearClearPoint(float position) { if (_currentScanMode != ScanMode.Logic) return false; return Mathf.Abs(position - clearPointPosition) < clearThreshold * 0.5f; } private void UpdateScanStatus(float position, bool isClear) { if (scanStatusText == null) return; if (_currentScanMode == ScanMode.Logic) { if (isClear) { scanStatusText.text = useMechanicalUiStyle ? $"{statusPrefix}{scanReadyToLockText}" : "✓ 信号稳定,可执行锁定"; } else { float distanceFromClear = Mathf.Abs(position - clearPointPosition); if (distanceFromClear < clearThreshold) { scanStatusText.text = useMechanicalUiStyle ? $"{statusPrefix}{scanFineTuneText}" : "信号趋稳中...继续微调"; } else { scanStatusText.text = useMechanicalUiStyle ? $"{statusPrefix}{scanSearchingText}" : "扫描中:寻找稳定窗口..."; } } } else { if (_fullyCovered) { scanStatusText.text = useMechanicalUiStyle ? $"{statusPrefix}{scanCompletedNotFoundText}" : "⚠ 扫描完成:未命中匹配波形"; } else { scanStatusText.text = useMechanicalUiStyle ? $"{statusPrefix}扫描进度:{_visitedCoverage * 100:F0}%" : $"扫描进度:{_visitedCoverage * 100:F0}%"; } } } private void OnLockButtonClicked() { if (!_isScanMode || _isLocked) return; if (_currentScanMode != ScanMode.Logic) return; float currentPosition = scanSlider != null ? scanSlider.value : 0.5f; if (!IsNearClearPoint(currentPosition)) return; _isLocked = true; _lockedPosition = currentPosition; _lockedTimestamp = GenerateTimestamp(); PlaySound(lockSound); if (lockButton != null) { lockButton.interactable = false; if (lockButtonText != null) { lockButtonText.text = "✓ 已锁定"; } } if (scanSlider != null) { scanSlider.interactable = false; } if (scanStatusText != null) { scanStatusText.text = useMechanicalUiStyle ? $"{statusPrefix}锁定完成 | 时间戳: {_lockedTimestamp}" : $"✓ 锁定完成 | 时间戳: {_lockedTimestamp}"; } if (primaryWaveform != null) { primaryWaveform.SetClarity(1f); primaryWaveform.SetNoiseAmount(0f); primaryWaveform.SetWaveType(WaveformType.Logic); } Debug.Log($"WaveformDetector: 锁定成功,时间戳: {_lockedTimestamp}"); } private void TriggerNotFound() { _notFoundTriggered = true; PlaySound(notFoundSound); if (scanStatusText != null) { scanStatusText.text = useMechanicalUiStyle ? $"{statusPrefix}{emotionNotFoundText}" : "⚠ 扫描完成:未检出原始情绪波形"; } if (scanSlider != null) { scanSlider.interactable = false; } Debug.Log("WaveformDetector: 情绪波形未找到"); } public void StopScanMode() { _isScanMode = false; _currentScanMode = ScanMode.None; _isLocked = false; if (scannerPanel != null) { scannerPanel.SetActive(false); } } #endregion #region 已访问区域追踪 private void CreateVisitedTexture() { _visitedTexture = new Texture2D(_segmentCount, 1, TextureFormat.RGBA32, false); _visitedTexture.filterMode = FilterMode.Point; _visitedTexture.wrapMode = TextureWrapMode.Clamp; if (visitedAreaImage != null) { visitedAreaImage.texture = _visitedTexture; } ResetVisitedArea(); } private void ResetVisitedArea() { for (int i = 0; i < _segmentCount; i++) { _visitedSegments[i] = false; } _visitedCoverage = 0f; _fullyCovered = false; UpdateVisitedAreaDisplay(); } private void MarkVisited(float position) { int segmentIndex = Mathf.Clamp((int)(position * _segmentCount), 0, _segmentCount - 1); int range = 3; for (int i = segmentIndex - range; i <= segmentIndex + range; i++) { if (i >= 0 && i < _segmentCount) { _visitedSegments[i] = true; } } int visitedCount = 0; for (int i = 0; i < _segmentCount; i++) { if (_visitedSegments[i]) visitedCount++; } _visitedCoverage = (float)visitedCount / _segmentCount; _fullyCovered = _visitedCoverage >= visitedCoverageRequired; } private void UpdateVisitedAreaDisplay() { if (_visitedTexture == null || visitedAreaImage == null) return; for (int i = 0; i < _segmentCount; i++) { // 使用序列化颜色,便于整体风格统一(CRT绿/暗底) _visitedTexture.SetPixel(i, 0, _visitedSegments[i] ? visitedColor : unvisitedColor); } _visitedTexture.Apply(); } #endregion #region 时间戳功能 private string GenerateTimestamp() { return $"07:12:{Random.Range(2, 10):D2}"; } public IEnumerator ShowTimestampWaveform() { if (string.IsNullOrEmpty(_lockedTimestamp)) { Debug.LogWarning("WaveformDetector: 没有锁定的时间戳"); yield break; } StopScanMode(); if (primaryWaveform != null) { var settings = waveformConfig.GetWaveformById("emotion"); primaryWaveform.ApplySettings(settings); primaryWaveform.SetClarity(1f); primaryWaveform.SetNoiseAmount(0f); primaryWaveform.SetVisible(true); } ShowResult(true, $"时间戳 [{_lockedTimestamp}] 的原始波形\n类型:情绪波形(悲伤/压抑)\n⚠ 注意:波形与当前输出不一致"); yield return new WaitForSeconds(0.5f); } #endregion #region 结果显示 public void ShowResult(bool isSuccess, string message) { if (resultPanel != null) resultPanel.SetActive(true); if (resultText != null) { resultText.text = message; if (useMechanicalUiStyle) { resultText.color = isSuccess ? uiOnlineColor : uiErrorColor; } else { resultText.color = isSuccess ? new Color(0.65f, 0.95f, 0.75f) : new Color(0.95f, 0.45f, 0.45f); } } } public void HideResult() { if (resultPanel != null) resultPanel.SetActive(false); } #endregion #region 波形变换 public IEnumerator TransformWaveform(string fromWaveformId, string toWaveformId, float duration = 1f) { if (waveformConfig == null || primaryWaveform == null) yield break; var fromSettings = waveformConfig.GetWaveformById(fromWaveformId); var toSettings = waveformConfig.GetWaveformById(toWaveformId); if (!_isShown) yield return Show(); primaryWaveform.ApplySettings(fromSettings); primaryWaveform.SetClarity(1f); primaryWaveform.SetNoiseAmount(0f); primaryWaveform.SetVisible(true); PlaySound(transformSound); primaryWaveform.StartTransform(fromSettings.waveType, toSettings.waveType); float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; float progress = elapsed / duration; primaryWaveform.UpdateTransformProgress(progress); Color currentPrimary = Color.Lerp(fromSettings.primaryColor, toSettings.primaryColor, progress); Color currentSecondary = Color.Lerp(fromSettings.secondaryColor, toSettings.secondaryColor, progress); primaryWaveform.SetColors(currentPrimary, currentSecondary); // 用透明度闪烁代替位置抖动 if (progress > 0.3f && progress < 0.7f) { float intensity = 1 - Mathf.Abs(progress - 0.5f) * 2f; float flicker = 0.7f + Mathf.Sin(progress * Mathf.PI * 20f) * intensity * 0.3f; primaryWaveform.SetAlpha(flicker); } else { primaryWaveform.SetAlpha(1f); } yield return null; } primaryWaveform.FinishTransform(); primaryWaveform.ApplySettings(toSettings); primaryWaveform.SetAlpha(1f); } #endregion #region 波形效果 private struct WaveformVisualState { public Color PrimaryColor; public Color SecondaryColor; public float Alpha; public float Clarity; public float ClarityPosition; public float ClarityWidth; public float NoiseAmount; } private WaveformVisualState CaptureWaveformState() { return new WaveformVisualState { PrimaryColor = primaryWaveform.PrimaryColor, SecondaryColor = primaryWaveform.SecondaryColor, Alpha = primaryWaveform.CurrentAlpha, Clarity = primaryWaveform.Clarity, ClarityPosition = primaryWaveform.ClarityPosition, ClarityWidth = primaryWaveform.ClarityWidth, NoiseAmount = primaryWaveform.NoiseAmount }; } private void RestoreWaveformState(WaveformVisualState state) { primaryWaveform.SetColors(state.PrimaryColor, state.SecondaryColor); primaryWaveform.SetClarity(state.Clarity); primaryWaveform.SetClarityPosition(state.ClarityPosition); primaryWaveform.SetClarityWidth(state.ClarityWidth); primaryWaveform.SetNoiseAmount(state.NoiseAmount); primaryWaveform.SetAlpha(state.Alpha); } /// /// 波形检测效果(闪烁) /// public IEnumerator WaveformCheckEffect(int blinkCount = 3, float blinkInterval = 0.15f) { if (primaryWaveform == null) yield break; WaveformVisualState originalState = CaptureWaveformState(); primaryWaveform.SetClarity(Mathf.Max(originalState.Clarity, 0.4f)); primaryWaveform.SetClarityWidth(Mathf.Max(originalState.ClarityWidth, 0.06f)); primaryWaveform.SetNoiseAmount(Mathf.Max(originalState.NoiseAmount, 0.25f)); for (int i = 0; i < blinkCount; i++) { yield return SweepAnalysis(blinkInterval, 0.05f, 0.95f); primaryWaveform.SetAlpha(0.6f); yield return new WaitForSeconds(blinkInterval * 0.3f); primaryWaveform.SetAlpha(originalState.Alpha); } RestoreWaveformState(originalState); } /// /// 屏蔽词检测效果(扫描线从左到右扫过) /// public IEnumerator WaveformSweepCheck(int sweepCount = 2, float sweepDuration = 0.25f) { if (primaryWaveform == null) yield break; // 使用扫描线进行检测,不改变波形颜色 primaryWaveform.SetScanLineColor(useMechanicalUiStyle ? sweepScanLineColor : new Color(0.2f, 0.75f, 0.85f, 0.85f)); for (int i = 0; i < sweepCount; i++) { yield return SweepWithScanLine(sweepDuration, 0.02f, 0.98f); yield return new WaitForSeconds(0.05f); } primaryWaveform.HideScanLine(); } /// /// 限幅检测效果(显示上下限线并闪烁) /// public IEnumerator WaveformRangeCheck(int pulseCount = 2, float pulseInterval = 0.12f) { if (primaryWaveform == null) yield break; // 显示上下限线 primaryWaveform.ShowLimitLines(); for (int i = 0; i < pulseCount; i++) { // 闪烁效果 yield return LimitLinesPulse(pulseInterval); yield return new WaitForSeconds(pulseInterval * 0.3f); } primaryWaveform.HideLimitLines(); } private IEnumerator SweepAnalysis(float duration, float start, float end) { float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; float t = Mathf.Clamp01(elapsed / duration); primaryWaveform.SetClarityPosition(Mathf.Lerp(start, end, t)); yield return null; } } /// /// 使用扫描线进行横向扫描 /// private IEnumerator SweepWithScanLine(float duration, float start, float end) { primaryWaveform.ShowScanLine(start); float elapsed = 0f; while (elapsed < duration) { elapsed += Time.deltaTime; float t = Mathf.Clamp01(elapsed / duration); float position = Mathf.Lerp(start, end, t); primaryWaveform.SetScanLinePosition(position); yield return null; } } /// /// 限幅线闪烁效果 /// private IEnumerator LimitLinesPulse(float duration) { float halfDuration = duration * 0.5f; float elapsed = 0f; // 淡入 while (elapsed < halfDuration) { elapsed += Time.deltaTime; float alpha = Mathf.Lerp(0.3f, 1f, elapsed / halfDuration); primaryWaveform.SetLimitLinesAlpha(alpha); yield return null; } // 淡出 elapsed = 0f; while (elapsed < halfDuration) { elapsed += Time.deltaTime; float alpha = Mathf.Lerp(1f, 0.3f, elapsed / halfDuration); primaryWaveform.SetLimitLinesAlpha(alpha); yield return null; } } private IEnumerator ProbeAt(float position, float duration) { primaryWaveform.SetClarityPosition(position); primaryWaveform.SetAlpha(0.6f); yield return new WaitForSeconds(duration); primaryWaveform.SetAlpha(1f); yield return new WaitForSeconds(duration * 0.5f); } /// /// 波形检测通过效果(绿色闪烁) /// public IEnumerator WaveformCheckPass(float duration = 0.4f) { yield return FlashWaveformColor(Color.green, duration); } /// /// 波形检测失败/警告效果(红色闪烁) /// public IEnumerator WaveformCheckFail(float duration = 0.6f) { yield return FlashWaveformColor(new Color(1f, 0.2f, 0.2f), duration); } /// /// 波形颜色闪烁效果 /// public IEnumerator FlashWaveformColor(Color flashColor, float duration, int flashCount = 3) { if (primaryWaveform == null) yield break; Color originalPrimary = GetCurrentWaveformPrimaryColor(); Color originalSecondary = GetCurrentWaveformSecondaryColor(); float flashTime = duration / (flashCount * 2); for (int i = 0; i < flashCount; i++) { primaryWaveform.SetColors(flashColor, flashColor); yield return new WaitForSeconds(flashTime); primaryWaveform.SetColors(originalPrimary, originalSecondary); yield return new WaitForSeconds(flashTime); } } private Color GetCurrentWaveformPrimaryColor() { if (primaryWaveform == null || waveformConfig == null) return Color.white; switch (primaryWaveform.WaveType) { case WaveformType.Logic: return waveformConfig.logicWaveform.primaryColor; case WaveformType.Emotion: return waveformConfig.emotionWaveform.primaryColor; case WaveformType.Joke: return waveformConfig.jokeWaveform.primaryColor; case WaveformType.FilterOutput: return waveformConfig.filterOutputWaveform.primaryColor; default: return Color.white; } } private Color GetCurrentWaveformSecondaryColor() { if (primaryWaveform == null || waveformConfig == null) return Color.gray; switch (primaryWaveform.WaveType) { case WaveformType.Logic: return waveformConfig.logicWaveform.secondaryColor; case WaveformType.Emotion: return waveformConfig.emotionWaveform.secondaryColor; case WaveformType.Joke: return waveformConfig.jokeWaveform.secondaryColor; case WaveformType.FilterOutput: return waveformConfig.filterOutputWaveform.secondaryColor; default: return Color.gray; } } #endregion #region 辅助方法 private void PlaySound(AudioClip clip) { if (_audioSource != null && clip != null) { _audioSource.PlayOneShot(clip); } } #endregion } }