using UnityEngine; using System.Collections.Generic; namespace AibisDream.MiniGame.Language { /// /// 候选粒子(可交互的主要粒子) /// public class CandidateParticle : TextParticle { [Header("粒子状态(目标/非目标,颜色由 Manager 配置,可与红蓝解绑)")] public bool isRed = false; // 当前是否为目标粒子(用于颜色/样式,可配成蓝) public bool isOrange = false; // 是否受到效果影响(橙色) public bool originalIsRed = false; // 原始是否为目标粒子(不变,用于字符逻辑) public bool hasEffect = false; // 是否有效果 public bool isTemporaryRed = false; // 临时红色状态 [Header("连接")] public List connections = new List(); [Header("效果")] public float changeSpeedMultiplier = 1f; public Vector2 vibrationOffset; public float vibrationPhase; public float effectTimer; public float effectAlpha; public float effectScale = 1f; [Header("焦虑效果")] public float anxietyShakePhase; public float anxietyShakeIntensity; public float anxietyFloatPhase; public float anxietyFloatSpeed; public Vector2 anxietyShakeOffset; public float anxietyFloatY; [Header("非目标干扰效果")] public float interferenceShakeIntensity = 0f; public float interferenceShakeSpeed = 8f; public float interferenceFloatAmplitude = 0.015f; private float interferenceShakePhase; private float interferenceFloatPhase; private Vector2 interferenceShakeOffset; private float interferenceFloatY; [Header("波形关联")] public float calmProgress = 0f; // 平静进度(0-1) public float resolvedColorProgress = 0f; // 飞向中央时的目标色渐变进度(0-1) public float dissolveProgress = 0f; // 消散进度(0-1) public WaveformPoint waveformPoint; // 关联的波形点 [Header("聚焦干扰")] [Range(0f, 1f)] public float focusInterferenceProgress = 0f; public Color focusInterferenceColor = new Color(1f, 0.1882353f, 0.3019608f, 1f); // #FF304D public float focusInterferenceShakeAmplitude = 0.16f; public float focusInterferenceShakeSpeed = 24f; private bool focusInterferenceUsesMarquee; private Color[] focusInterferenceMarqueeColors; private int focusInterferenceMarqueeIndex; private float focusInterferenceMarqueeStepDuration = 0.18f; // 颜色配置(由Manager设置) private Color targetColor = new Color(1f, 0.39f, 0.39f, 1f); // 目标粒子颜色 private Color nonTargetColor = new Color(1f, 0.1882353f, 0.3019608f, 1f); // 非目标粒子固定红色 #FF304D private Color propagationColor = new Color(1f, 0.59f, 0.2f, 1f); // 传播颜色 private Color calmColor = new Color(0.3607843f, 0.945098f, 0.8156863f, 1f); // 最终目标色 #5CF1D0 private Color[] nonTargetMarqueeColors; private int nonTargetMarqueeIndex; private float nonTargetMarqueeStepDuration = 0.18f; // 字体样式配置(由Manager设置) private float targetFontSize = 3.5f; private float nonTargetFontSize = 2.5f; private bool targetBold = true; private bool nonTargetBold = false; protected override void Awake() { base.Awake(); // 与场景示例文字保持一致:固定使用完整顶点 Alpha,不引入随机亮度差异。 baseAlpha = 1f; alpha = baseAlpha; size = nonTargetFontSize; // 默认使用非目标粒子大小 velocity = new Vector2(Random.Range(-0.5f, 0.5f), Random.Range(-0.5f, 0.5f)); changeInterval = Random.Range(1f, 2.5f); interferenceShakePhase = Random.Range(0f, Mathf.PI * 2f); interferenceFloatPhase = Random.Range(0f, Mathf.PI * 2f); if (textMesh != null) { textMesh.fontSize = size; UpdateFontStyle(); } } protected override void Update() { base.Update(); // 更新效果动画 if (hasEffect) { UpdateEffectAnimation(); } // 更新焦虑效果 if (isRed && (anxietyShakeIntensity > 0f || focusInterferenceProgress > 0f)) { UpdateAnxietyEffect(); } UpdateFocusInterferenceCharacters(); // 非目标粒子干扰抖动 if (!isRed && !isCalmed && interferenceShakeIntensity > 0) { UpdateInterferenceEffect(); } } private void UpdateEffectAnimation() { if (effectTimer > 0) { effectTimer -= Time.deltaTime; // 振动效果 vibrationPhase += Time.deltaTime * 15f; float vibrationIntensity = 0.02f; vibrationOffset = new Vector2( Mathf.Cos(vibrationPhase) * vibrationIntensity, Mathf.Sin(vibrationPhase * 1.3f) * vibrationIntensity ); // 透明度动画(0-1 范围) effectAlpha = Mathf.Lerp(0.6f, 1f, (Mathf.Sin(Time.time * 3f) + 1f) / 2f); // 缩放动画 effectScale = Mathf.Lerp(0.95f, 1.05f, (Mathf.Sin(Time.time * 4.5f) + 1f) / 2f); } } private void UpdateAnxietyEffect() { // 常规焦虑随稳定度消退;聚焦干扰不受稳定度影响,稳定后的字也能被重新强烈扰动。 float normalShake = anxietyShakeIntensity * (1f - calmProgress) * 0.01f; float disruptionShake = focusInterferenceShakeAmplitude * focusInterferenceProgress; float shakeIntensity = normalShake + disruptionShake; float floatAmplitude = 0.03f * (1f - calmProgress) + disruptionShake * 0.35f; if (calmProgress < 0.9f || focusInterferenceProgress > 0f) { anxietyShakePhase += Time.deltaTime * Mathf.Lerp( 10f, focusInterferenceShakeSpeed, focusInterferenceProgress); anxietyFloatPhase += Mathf.Max(anxietyFloatSpeed, Time.deltaTime * 8f * focusInterferenceProgress); } // 计算抖动偏移 anxietyShakeOffset = new Vector2( Mathf.Cos(anxietyShakePhase) * shakeIntensity + Mathf.Sin(anxietyShakePhase * 1.7f) * shakeIntensity * 0.5f, Mathf.Sin(anxietyShakePhase * 1.3f) * shakeIntensity + Mathf.Cos(anxietyShakePhase * 0.9f) * shakeIntensity * 0.5f ); // 计算漂浮偏移 anxietyFloatY = Mathf.Sin(anxietyFloatPhase) * floatAmplitude; } protected override void UpdateVisuals() { if (textMesh == null) return; // 更新字体样式(大小和加粗) UpdateFontStyle(); // 计算显示位置(叠加干扰抖动) Vector3 displayOffset = (Vector3)(vibrationOffset + anxietyShakeOffset + interferenceShakeOffset) + Vector3.up * (anxietyFloatY + interferenceFloatY); textMesh.transform.localPosition = displayOffset; // 计算颜色和透明度 Color color = Color.white; float displayAlpha = alpha; if (hasEffect && effectTimer > 0) { displayAlpha = effectAlpha; textMesh.transform.localScale = Vector3.one * effectScale; } // 根据消散进度减少透明度 displayAlpha *= (1 - dissolveProgress); if (isRed) { // 颜色进度与稳定度解耦:飞向中央时平滑过渡,之后保持固定目标色。 Color resolved = Color.Lerp(targetColor, calmColor, Mathf.Clamp01(resolvedColorProgress)); Color interferenceColor = focusInterferenceUsesMarquee ? GetMarqueeColor( focusInterferenceMarqueeColors, focusInterferenceMarqueeIndex, focusInterferenceMarqueeStepDuration, focusInterferenceColor) : focusInterferenceColor; Color solid = Color.Lerp( resolved, interferenceColor, Mathf.Clamp01(focusInterferenceProgress)); color = new Color(solid.r, solid.g, solid.b, displayAlpha); } else if (isOrange) { // 传播效果颜色 color = new Color(propagationColor.r, propagationColor.g, propagationColor.b, displayAlpha); } else { // 笑话、逃避与愤怒在屏幕粒子中统一使用固定红色。 Color marqueeColor = GetNonTargetMarqueeColor(); color = new Color(marqueeColor.r, marqueeColor.g, marqueeColor.b, displayAlpha); } // 面板上的目标/非目标色直接写到顶点色(与示例 TMP 相同,不改材质 FaceColor) textMesh.color = color; } /// /// 目标粒子:从本轮本地化默认池随机;非目标粒子:从笑话/干扰短句字符中随机。 /// 使用 IsTarget 判断,与红蓝颜色解绑。 /// protected override string GetNextDisplayChar() { if (TryGetOverridePoolChar(out string overrideChar)) return overrideChar; return IsTarget ? GetRandomChar() : GetRandomCharFromPhrases(); } /// /// 是否为目标粒子(由 originalIsRed 表示,与显示颜色解绑) /// private bool IsTarget => originalIsRed; private void UpdateInterferenceEffect() { interferenceShakePhase += Time.deltaTime * interferenceShakeSpeed; interferenceFloatPhase += Time.deltaTime * interferenceShakeSpeed * 0.3f; float intensity = interferenceShakeIntensity; interferenceShakeOffset = new Vector2( Mathf.Sin(interferenceShakePhase) * intensity + Mathf.Sin(interferenceShakePhase * 2.3f) * intensity * 0.4f, Mathf.Cos(interferenceShakePhase * 1.7f) * intensity * 0.7f + Mathf.Sin(interferenceShakePhase * 0.6f) * intensity * 0.3f ); interferenceFloatY = Mathf.Sin(interferenceFloatPhase) * interferenceFloatAmplitude; } private void UpdateFocusInterferenceCharacters() { if (focusInterferenceProgress <= 0f || !HasOverrideCharacterPool || (!isStatic && !isCalmed)) { return; } changeTimer -= Time.deltaTime; if (changeTimer > 0f) return; currentChar = GetNextDisplayChar(); UpdateText(); changeTimer = Mathf.Max(0.02f, changeInterval); } public void SetInterferenceParams(float shakeIntensity, float shakeSpeed, float changeIntv, float floatAmplitude) { interferenceShakeIntensity = shakeIntensity; interferenceShakeSpeed = shakeSpeed; interferenceFloatAmplitude = floatAmplitude; if (!isRed) { changeInterval = Mathf.Max(0.05f, changeIntv * Random.Range(0.7f, 1.3f)); } } public void InitializeAnxietyEffect() { anxietyShakePhase = Random.Range(0f, Mathf.PI * 2f); anxietyShakeIntensity = Random.Range(2f, 4f); anxietyFloatPhase = Random.Range(0f, Mathf.PI * 2f); anxietyFloatSpeed = Random.Range(0.02f, 0.04f); } public void ApplyEffect(float duration = 1f) { hasEffect = true; effectTimer = duration; isOrange = true; isRed = false; changeSpeedMultiplier = 3f; vibrationPhase = Random.Range(0f, Mathf.PI * 2f); } public void RemoveEffect() { hasEffect = false; isOrange = false; isRed = false; changeSpeedMultiplier = 1f; vibrationOffset = Vector2.zero; effectTimer = 0; } /// /// 设置粒子颜色配置 /// public void SetColors(Color target, Color nonTarget, Color propagation, Color calm) { targetColor = target; nonTargetColor = nonTarget; propagationColor = propagation; calmColor = calm; } public void SetNonTargetMarquee( Color[] colors, int particleIndex, float stepDuration) { nonTargetMarqueeColors = colors; nonTargetMarqueeIndex = Mathf.Max(0, particleIndex); nonTargetMarqueeStepDuration = Mathf.Max(0.02f, stepDuration); } private Color GetNonTargetMarqueeColor() { return GetMarqueeColor( nonTargetMarqueeColors, nonTargetMarqueeIndex, nonTargetMarqueeStepDuration, nonTargetColor); } private static Color GetMarqueeColor( Color[] colors, int particleIndex, float stepDuration, Color fallback) { if (colors == null || colors.Length == 0) return fallback; int step = Mathf.FloorToInt(Time.time / Mathf.Max(0.02f, stepDuration)); int index = (Mathf.Max(0, particleIndex) + step) % colors.Length; return colors[index]; } public void SetFocusInterference( Color color, float shakeAmplitude, float shakeSpeed = 24f) { focusInterferenceUsesMarquee = false; focusInterferenceColor = color; focusInterferenceShakeAmplitude = Mathf.Max(0f, shakeAmplitude); focusInterferenceShakeSpeed = Mathf.Max(0f, shakeSpeed); } public void SetFocusInterferenceMarquee( Color[] colors, int particleIndex, float stepDuration, float shakeAmplitude, float shakeSpeed = 24f) { focusInterferenceUsesMarquee = colors != null && colors.Length > 0; focusInterferenceMarqueeColors = colors; focusInterferenceMarqueeIndex = Mathf.Max(0, particleIndex); focusInterferenceMarqueeStepDuration = Mathf.Max(0.02f, stepDuration); focusInterferenceShakeAmplitude = Mathf.Max(0f, shakeAmplitude); focusInterferenceShakeSpeed = Mathf.Max(0f, shakeSpeed); } public void ClearFocusInterference() { focusInterferenceProgress = 0f; focusInterferenceUsesMarquee = false; anxietyShakeOffset = Vector2.zero; anxietyFloatY = 0f; } /// /// 设置字体样式配置 /// public void SetFontStyles(float targetSize, float nonTargetSize, bool targetIsBold, bool nonTargetIsBold) { targetFontSize = targetSize; nonTargetFontSize = nonTargetSize; targetBold = targetIsBold; nonTargetBold = nonTargetIsBold; // 立即应用 if (textMesh != null) { UpdateFontStyle(); } } /// /// 更新字体样式(根据粒子状态) /// private void UpdateFontStyle() { if (textMesh == null) return; // 根据是否为目标粒子设置大小和加粗 if (isRed) { textMesh.fontSize = targetFontSize; textMesh.fontStyle = targetBold ? TMPro.FontStyles.Bold : TMPro.FontStyles.Normal; } else { textMesh.fontSize = nonTargetFontSize; textMesh.fontStyle = nonTargetBold ? TMPro.FontStyles.Bold : TMPro.FontStyles.Normal; } } } }