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;
}
}
}
}