Files
aibis-dream/Assets/Scripts/MiniGame/HuoShan/Language/CandidateParticle.cs
T

437 lines
16 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 UnityEngine;
using System.Collections.Generic;
namespace AibisDream.MiniGame.Language
{
/// <summary>
/// 候选粒子(可交互的主要粒子)
/// </summary>
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<CandidateParticle> connections = new List<CandidateParticle>();
[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.2392157f, 0.3215686f, 1f);
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(0.2f, 0.78f, 1f, 1f); // 非目标粒子颜色
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;
}
/// <summary>
/// 目标粒子:chineseChars 随机单字;非目标粒子:从笑话/干扰短句字符中随机。
/// 使用 IsTarget 判断,与红蓝颜色解绑。
/// </summary>
protected override string GetNextDisplayChar()
{
if (TryGetOverridePoolChar(out string overrideChar))
return overrideChar;
return IsTarget ? GetRandomChar() : GetRandomCharFromPhrases();
}
/// <summary>
/// 是否为目标粒子(由 originalIsRed 表示,与显示颜色解绑)
/// </summary>
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 ||
string.IsNullOrEmpty(overrideCharPool) ||
(!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;
}
/// <summary>
/// 设置粒子颜色配置
/// </summary>
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;
}
/// <summary>
/// 设置字体样式配置
/// </summary>
public void SetFontStyles(float targetSize, float nonTargetSize, bool targetIsBold, bool nonTargetIsBold)
{
targetFontSize = targetSize;
nonTargetFontSize = nonTargetSize;
targetBold = targetIsBold;
nonTargetBold = nonTargetIsBold;
// 立即应用
if (textMesh != null)
{
UpdateFontStyle();
}
}
/// <summary>
/// 更新字体样式(根据粒子状态)
/// </summary>
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;
}
}
}
}