fix(huoshan): 修复多轮表达重叠并支持非目标粒子数配置

Made-with: Cursor
This commit is contained in:
2026-03-27 14:26:42 +08:00
parent 618a9fc0df
commit f67641407d
3 changed files with 366 additions and 48 deletions
@@ -100,7 +100,8 @@ namespace AibisDream
/// <param name="anxietyPhrases">干扰短句列表(笑话等,非目标粒子从中随机取字符)</param>
/// <param name="targetSentence">目标句子</param>
/// <param name="completionNodeName">完成时触发的对话节点名称(可选)</param>
public void StartSystem(List<string> anxietyPhrases, string targetSentence, string completionNodeName = null)
/// <param name="nonTargetParticleTotal">非目标候选粒子总数;&lt;0 时使用 Inspector 默认候选池大小</param>
public void StartSystem(List<string> anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1)
{
if (particleManager != null)
{
@@ -108,17 +109,17 @@ namespace AibisDream
var phrases = anxietyPhrases ?? defaultAnxietyPhrases;
var sentence = targetSentence ?? defaultTargetSentence;
particleManager.InitializeSystem(phrases, sentence, completionNodeName);
particleManager.InitializeSystem(phrases, sentence, completionNodeName, nonTargetParticleTotal);
}
}
/// <summary>
/// 启动粒子系统(字符串数组版本,方便 Yarn 调用)
/// </summary>
public void StartSystem(string[] anxietyPhrases, string targetSentence, string completionNodeName = null)
public void StartSystem(string[] anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1)
{
List<string> phrasesList = anxietyPhrases != null ? new List<string>(anxietyPhrases) : null;
StartSystem(phrasesList, targetSentence, completionNodeName);
StartSystem(phrasesList, targetSentence, completionNodeName, nonTargetParticleTotal);
}
/// <summary>
@@ -131,6 +132,16 @@ namespace AibisDream
yield return particleManager.FadeInReleaseLogIfNeeded(duration);
}
/// <summary>
/// 再次 <c>start_expression</c> 前淡出上一轮粒子与连线(若尚未初始化则立即结束)。
/// duration≤0 时使用 LanguageParticleManager 上的默认时长。
/// </summary>
public IEnumerator FadeOutParticlesBeforeNewRound(float duration = 0f)
{
if (particleManager == null) yield break;
yield return StartCoroutine(particleManager.FadeOutBeforeNewExpressionRound(duration));
}
/// <summary>
/// 等待表达流程就绪(开场表现 + 火山表达panel 播完)
/// </summary>
@@ -113,6 +113,16 @@ namespace AibisDream.MiniGame.Language
[SerializeField] private TMP_Text integrationProgressText;
[SerializeField] private TMP_Text interferenceCountText;
[Header("完成/确认阶段 — 屏幕内显示范围")]
[Tooltip("为真时,完成连线后(含确认按钮、聚焦、稳定化)目标字符位置会被限制在裁剪区域内,避免超出电视机屏幕可视区")]
[SerializeField] private bool clampCompletionPhasePositions = true;
[Tooltip("可选:场景中一块与屏幕内框对齐的 RectTransformWorld Space Canvas 子物体即可)。不指定则用粒子用的 movementBoundscanvasSize - textMargin")]
[SerializeField] private RectTransform completionScreenClipRect;
[Tooltip("在裁剪区基础上再向内缩进的世界单位距离,避免放大后的字半个挂在边外")]
[SerializeField] private float completionClipEdgePadding = 0.2f;
[Tooltip("将目标粒子拉回屏幕裁剪区时的平滑时间(越大越柔和,略增加拖尾感)")]
[SerializeField, Range(0.02f, 0.55f)] private float completionPositionSmoothTime = 0.2f;
[Header("完成阶段 UI")]
[SerializeField] private GameObject languageDeepPanel1;
[SerializeField] private GameObject languageDeepPanel2;
@@ -126,6 +136,8 @@ namespace AibisDream.MiniGame.Language
[SerializeField] private float focusZoomScale = 1.6f;
[SerializeField] private float focusMoveDuration = 1.2f;
[SerializeField] private float focusFadeDuration = 1f;
[Tooltip("再次 start_expression 时,若上一轮粒子/UI 仍在,先淡出再重开;协程 duration≤0 时用此值(秒)")]
[SerializeField] private float restartExpressionFadeDuration = 0.55f;
[SerializeField] private float probabilityDuration = 3.5f;
[SerializeField] private float predictionSequenceGap = 0.4f;
[SerializeField] private float spreadDistance = 0.35f;
@@ -187,6 +199,8 @@ namespace AibisDream.MiniGame.Language
private bool allowInput = true;
private float focusTimer = 0f;
private Dictionary<CandidateParticle, TargetFocusVisual> focusVisuals = new Dictionary<CandidateParticle, TargetFocusVisual>();
/// <summary>完成阶段屏幕钳制用 SmoothDamp 速度缓存(按粒子)</summary>
private readonly Dictionary<CandidateParticle, Vector3> completionClampSmoothVelocity = new Dictionary<CandidateParticle, Vector3>();
private Vector3 focusCentroid;
private List<string> finalTargetCharacters = new List<string>();
private List<Vector3> finalTargetPositions = new List<Vector3>();
@@ -236,6 +250,17 @@ namespace AibisDream.MiniGame.Language
// 是否已经初始化
private bool isInitialized = false;
/// <summary>Inspector 中的候选粒子总数,用于未在 Yarn 中指定非目标数量时恢复默认</summary>
private int defaultCandidateCountFromInspector;
private bool hasCapturedDefaultCandidateCount;
private void CaptureDefaultCandidateCountFromInspector()
{
if (hasCapturedDefaultCandidateCount) return;
defaultCandidateCountFromInspector = candidateCount;
hasCapturedDefaultCandidateCount = true;
}
private void Start()
{
// Start 中不再自动初始化,等待外部调用 InitializeSystem
@@ -247,8 +272,24 @@ namespace AibisDream.MiniGame.Language
/// <param name="phrases">焦虑短句列表</param>
/// <param name="sentence">目标句子</param>
/// <param name="dialogNode">完成时触发的对话节点</param>
public void InitializeSystem(List<string> phrases, string sentence, string dialogNode = null)
/// <param name="nonTargetParticleTotal">
/// 非目标候选粒子总数(蓝/干扰侧可交互粒子数量)。≥0 时候选池大小 = 目标句字数 + 该值;&lt;0 时使用 Inspector 的 Candidate Count。
/// </param>
public void InitializeSystem(List<string> phrases, string sentence, string dialogNode = null, int nonTargetParticleTotal = -1)
{
CaptureDefaultCandidateCountFromInspector();
string useSentence = string.IsNullOrEmpty(sentence) ? "这是一个测试示例" : sentence;
if (nonTargetParticleTotal >= 0)
{
candidateCount = Mathf.Max(useSentence.Length + nonTargetParticleTotal, useSentence.Length);
}
else
{
candidateCount = defaultCandidateCountFromInspector;
}
// 如果是第一次初始化,先执行基础初始化
if (!isInitialized)
{
@@ -280,22 +321,120 @@ namespace AibisDream.MiniGame.Language
isInitialized = true;
}
else
{
EnsureCandidatePoolSize(candidateCount);
}
// 设置游戏配置
anxietyPhrases = phrases ?? new List<string>();
targetSentence = sentence ?? "这是一个测试示例";
targetSentence = useSentence;
completionDialogNode = dialogNode ?? "";
// 设置全局焦虑短句配置
TextParticle.SetAnxietyPhrases(anxietyPhrases);
// 根据目标句子更新目标粒子数量
redParticleCount = Mathf.Min(sentence.Length, candidateCount);
redParticleCount = Mathf.Min(targetSentence.Length, candidateCount);
// 重新初始化游戏状态并播放入场动画
ResetGame(true);
}
/// <summary>
/// 在再次 <c>start_expression</c> 前调用:若系统已初始化过,则淡出当前粒子、连线与相关 UI,避免与新一轮重叠。
/// duration≤0 时使用 <see cref="restartExpressionFadeDuration"/>。
/// </summary>
public IEnumerator FadeOutBeforeNewExpressionRound(float duration)
{
if (!isInitialized)
yield break;
float d = duration > 0f ? duration : restartExpressionFadeDuration;
// 火山释放 log 与文字同时叠在屏幕上时,先把 log 藏起来,等文字淡出结束后再由 FadeInReleaseLogIfNeeded 出现
HideReleaseLogImmediate();
if (entranceRoutine != null)
{
StopCoroutine(entranceRoutine);
entranceRoutine = null;
}
if (expressionPanelRoutine != null)
{
StopCoroutine(expressionPanelRoutine);
expressionPanelRoutine = null;
}
if (confirmTimelineRoutine != null)
{
StopCoroutine(confirmTimelineRoutine);
confirmTimelineRoutine = null;
}
DOTween.Kill(this);
foreach (var p in candidateParticles)
{
if (p != null)
{
DOTween.Kill(p);
p.transform.DOKill();
p.velocity = Vector2.zero;
p.isStatic = true;
}
}
foreach (var f in floatingParticles)
{
if (f != null)
{
DOTween.Kill(f);
f.transform.DOKill();
f.velocity = Vector2.zero;
f.isCalmed = true;
}
}
interferenceLogicSuspended = true;
allowInput = false;
isCompleted = false;
completionPhase = CompletionPhase.None;
DestroyFocusVisualDecorationsAndClear();
DOTween.To(() => fadeOutAlpha, v => fadeOutAlpha = v, 0f, d)
.SetEase(Ease.OutQuad)
.SetTarget(this);
if (connectionRenderer != null)
{
DOTween.To(() => connectionRenderer.NonTargetAlphaScale, v => connectionRenderer.NonTargetAlphaScale = v, 0f, d)
.SetEase(Ease.OutQuad)
.SetTarget(this);
DOTween.To(() => connectionRenderer.TargetAlphaScale, v => connectionRenderer.TargetAlphaScale = v, 0f, d)
.SetEase(Ease.OutQuad)
.SetTarget(this);
}
foreach (var p in candidateParticles)
{
if (p != null)
FadeOutTextParticle(p, d);
}
foreach (var f in floatingParticles)
{
if (f != null)
FadeOutTextParticle(f, d);
}
SetStatusUIVisibility(false, d);
if (focusSequenceButton != null && focusSequenceButton.gameObject.activeSelf)
FadeOutUIElement(focusSequenceButton.gameObject, d);
FadeOutUIElement(languageDeepPanel1, d);
FadeOutUIElement(languageDeepPanel2, d);
yield return new WaitForSeconds(d);
fadeOutAlpha = 0f;
}
/// <summary>
/// 在打开视图前设置所有初始状态(panels、button 等不激活)
/// </summary>
@@ -378,6 +517,7 @@ namespace AibisDream.MiniGame.Language
isCompleted = false;
completionPhase = CompletionPhase.None;
allowInput = false;
ClearCompletionClampSmoothVelocity();
}
private void InitializeCanvas()
@@ -463,23 +603,7 @@ namespace AibisDream.MiniGame.Language
// 创建候选粒子
for (int i = 0; i < candidateCount; i++)
{
Vector3 pos = GetRandomPositionInBounds();
GameObject obj = CreateParticleObject(pos, $"CandidateParticle_{i}");
CandidateParticle particle = obj.AddComponent<CandidateParticle>();
particle.SetFont(chineseFontAsset);
particle.SetColors(targetParticleColor, nonTargetParticleColor, nonTargetParticleColor, targetCalmColor);
particle.SetFontStyles(targetParticleFontSize, nonTargetParticleFontSize, targetParticleBold, nonTargetParticleBold);
particle.SetInterferenceParams(nonTargetShakeIntensity, nonTargetShakeSpeed, nonTargetChangeInterval, nonTargetFloatAmplitude);
SetParticleBounds(particle);
// 设置发光效果(目标粒子和非目标粒子使用不同强度)
if (enableBloomGlow)
{
particle.SetGlowSettings(true, textGlowIntensity, nonTargetParticleColor);
particle.SetUnderlaySettings(glowDilate, glowSoftness);
}
candidateParticles.Add(particle);
AddOneCandidateParticle(i);
}
// 选择红色粒子
@@ -501,6 +625,52 @@ namespace AibisDream.MiniGame.Language
return obj;
}
private void AddOneCandidateParticle(int index)
{
Vector3 pos = GetRandomPositionInBounds();
GameObject obj = CreateParticleObject(pos, $"CandidateParticle_{index}");
CandidateParticle particle = obj.AddComponent<CandidateParticle>();
particle.SetFont(chineseFontAsset);
particle.SetColors(targetParticleColor, nonTargetParticleColor, nonTargetParticleColor, targetCalmColor);
particle.SetFontStyles(targetParticleFontSize, nonTargetParticleFontSize, targetParticleBold, nonTargetParticleBold);
particle.SetInterferenceParams(nonTargetShakeIntensity, nonTargetShakeSpeed, nonTargetChangeInterval, nonTargetFloatAmplitude);
SetParticleBounds(particle);
if (enableBloomGlow)
{
particle.SetGlowSettings(true, textGlowIntensity, nonTargetParticleColor);
particle.SetUnderlaySettings(glowDilate, glowSoftness);
}
candidateParticles.Add(particle);
}
/// <summary>
/// 增删候选粒子以匹配目标数量(用于 Yarn 动态指定非目标粒子总数后复用同一视图)
/// </summary>
private void EnsureCandidatePoolSize(int targetSize)
{
if (targetSize < 0) targetSize = 0;
candidateCount = targetSize;
while (candidateParticles.Count < targetSize)
{
AddOneCandidateParticle(candidateParticles.Count);
}
while (candidateParticles.Count > targetSize)
{
int last = candidateParticles.Count - 1;
CandidateParticle p = candidateParticles[last];
candidateParticles.RemoveAt(last);
if (p != null)
{
p.transform.DOKill();
Destroy(p.gameObject);
}
}
}
private Vector3 GetRandomPositionInBounds()
{
if (layoutShape == LayoutShape.Circle)
@@ -686,6 +856,96 @@ namespace AibisDream.MiniGame.Language
UpdateRenderers();
}
private void LateUpdate()
{
if (!isInitialized || !clampCompletionPhasePositions || !isCompleted)
return;
if (completionPhase == CompletionPhase.None)
return;
// 连线刚完成、等待确认时保持粒子当前世界坐标,避免 GetCompletionClipBounds(含 padding/屏幕 Rect
// 比游玩用 movementBounds 更紧时,在 Completed 首帧 LateUpdate 产生生硬瞬移。
if (completionPhase == CompletionPhase.Completed)
return;
Bounds clip = GetCompletionClipBounds();
if (clip.size.x < 0.01f || clip.size.y < 0.01f)
return;
float dt = Time.deltaTime;
if (dt < 1e-6f)
dt = 1e-6f;
float smooth = Mathf.Max(0.0001f, completionPositionSmoothTime);
for (int i = 0; i < targetParticles.Count; i++)
{
var p = targetParticles[i];
if (p == null) continue;
Vector3 cur = p.transform.position;
Vector3 desired = ClampPositionToBounds(cur, clip);
if (!completionClampSmoothVelocity.TryGetValue(p, out Vector3 vel))
vel = Vector3.zero;
Vector3 next = Vector3.SmoothDamp(cur, desired, ref vel, smooth, Mathf.Infinity, dt);
completionClampSmoothVelocity[p] = vel;
p.transform.position = next;
}
}
private void ClearCompletionClampSmoothVelocity()
{
completionClampSmoothVelocity.Clear();
}
/// <summary>
/// 完成阶段使用的裁剪范围:优先用 Inspector 指定的屏幕 Rect,否则与粒子运动边界一致。
/// </summary>
private Bounds GetCompletionClipBounds()
{
Bounds b;
if (completionScreenClipRect != null)
{
var corners = new Vector3[4];
completionScreenClipRect.GetWorldCorners(corners);
float minX = float.MaxValue, maxX = float.MinValue;
float minY = float.MaxValue, maxY = float.MinValue;
for (int c = 0; c < 4; c++)
{
minX = Mathf.Min(minX, corners[c].x);
maxX = Mathf.Max(maxX, corners[c].x);
minY = Mathf.Min(minY, corners[c].y);
maxY = Mathf.Max(maxY, corners[c].y);
}
float z = corners[0].z;
Vector3 center = new Vector3((minX + maxX) * 0.5f, (minY + maxY) * 0.5f, z);
Vector3 size = new Vector3(Mathf.Max(0f, maxX - minX), Mathf.Max(0f, maxY - minY), 0f);
b = new Bounds(center, size);
}
else
{
b = movementBounds;
}
if (completionClipEdgePadding > 0f)
{
Bounds shrunk = b;
shrunk.Expand(-completionClipEdgePadding);
if (shrunk.size.x >= 0.01f && shrunk.size.y >= 0.01f)
{
b = shrunk;
}
}
return b;
}
private static Vector3 ClampPositionToBounds(Vector3 worldPos, Bounds b)
{
return new Vector3(
Mathf.Clamp(worldPos.x, b.min.x, b.max.x),
Mathf.Clamp(worldPos.y, b.min.y, b.max.y),
worldPos.z
);
}
private void UpdateRenderers()
{
// 更新连接线渲染器
@@ -833,7 +1093,7 @@ namespace AibisDream.MiniGame.Language
focusTimer = 0f;
fadeOutAlpha = 1f;
allowInput = false;
focusVisuals.Clear();
DestroyFocusVisualDecorationsAndClear();
finalTargetCharacters.Clear();
finalTargetPositions.Clear();
focusTweensCompleted = false;
@@ -860,9 +1120,11 @@ namespace AibisDream.MiniGame.Language
float totalWidth = spacing * Mathf.Max(0, finalTargetCharacters.Count - 1);
float startX = -totalWidth / 2f;
finalTargetPositions.Clear();
Bounds clip = GetCompletionClipBounds();
for (int i = 0; i < finalTargetCharacters.Count; i++)
{
finalTargetPositions.Add(displayCenter + new Vector3(startX + i * spacing, 0f, 0f));
Vector3 pos = displayCenter + new Vector3(startX + i * spacing, 0f, 0f);
finalTargetPositions.Add(ClampPositionToBounds(pos, clip));
}
}
@@ -968,6 +1230,8 @@ namespace AibisDream.MiniGame.Language
focusSequenceButton.interactable = false;
}
ClearCompletionClampSmoothVelocity();
completionPhase = CompletionPhase.Focusing;
focusTimer = 0f;
focusTweensCompleted = false;
@@ -1018,8 +1282,9 @@ namespace AibisDream.MiniGame.Language
focusCentroid = ComputeCentroid(orderedRedParticles);
Vector3 focusTargetCenter = worldCanvas != null ? worldCanvas.transform.position : Vector3.zero;
Bounds focusClipBounds = GetCompletionClipBounds();
focusVisuals.Clear();
DestroyFocusVisualDecorationsAndClear();
int tweenTargetCount = orderedRedParticles.Count;
int completedTweens = 0;
@@ -1031,6 +1296,8 @@ namespace AibisDream.MiniGame.Language
Vector3 spreadOffset = new Vector3((i - indexCenter) * spreadDistance, Mathf.Sin(i * 1.4f) * spreadDistance * 0.6f, 0f);
Vector3 focusPosition = focusTargetCenter + offset * focusZoomScale + spreadOffset;
Vector3 finalPosition = finalTargetPositions.Count > i ? finalTargetPositions[i] : focusTargetCenter;
focusPosition = ClampPositionToBounds(focusPosition, focusClipBounds);
finalPosition = ClampPositionToBounds(finalPosition, focusClipBounds);
particle.transform.DOKill();
particle.transform.DOMove(focusPosition, focusMoveDuration)
@@ -1213,29 +1480,35 @@ namespace AibisDream.MiniGame.Language
}
}
private void CompleteFocusSequence()
private void DestroyFocusVisualDecorationsAndClear()
{
foreach (var visual in focusVisuals.Values)
{
if (visual == null)
continue;
if (visual.ringObject != null)
{
Destroy(visual.ringObject);
}
if (visual.probabilityLabel != null)
{
Destroy(visual.probabilityLabel.gameObject);
}
if (visual.particle != null)
{
visual.particle.transform.DOScale(Vector3.one * focusZoomScale, 0.3f).SetEase(Ease.OutQuad);
visual.particle.isStatic = true;
visual.particle.changeSpeedMultiplier = 1f;
}
}
focusVisuals.Clear();
}
private void CompleteFocusSequence()
{
var particlesToScale = new List<CandidateParticle>();
foreach (var visual in focusVisuals.Values)
{
if (visual?.particle != null)
particlesToScale.Add(visual.particle);
}
DestroyFocusVisualDecorationsAndClear();
foreach (var p in particlesToScale)
{
p.transform.DOScale(Vector3.one * focusZoomScale, 0.3f).SetEase(Ease.OutQuad);
p.isStatic = true;
p.changeSpeedMultiplier = 1f;
}
if (connectionRenderer != null)
{
@@ -1330,6 +1603,21 @@ namespace AibisDream.MiniGame.Language
});
}
/// <summary>
/// 立即隐藏火山释放 log(无动画),用于新一轮 start 前先淡出文字、再单独淡入 log。
/// </summary>
private void HideReleaseLogImmediate()
{
if (releaseLogSpriteRenderer == null)
return;
DOTween.Kill(releaseLogSpriteRenderer);
releaseLogSpriteRenderer.enabled = false;
releaseLogSpriteRenderer.gameObject.SetActive(false);
Color c = releaseLogSpriteRenderer.color;
c.a = 0f;
releaseLogSpriteRenderer.color = c;
}
/// <summary>
/// 若火山释放 log 界面已淡出,则淡入显示(start_expression 时调用,确保流程开始前界面可见)
/// </summary>
@@ -1441,7 +1729,7 @@ namespace AibisDream.MiniGame.Language
fadeOutAlpha = 1f;
allowInput = !playEntranceEffect;
orderedRedParticles.Clear();
focusVisuals.Clear();
DestroyFocusVisualDecorationsAndClear();
finalTargetCharacters.Clear();
finalTargetPositions.Clear();
previousConnections.Clear();
@@ -1449,6 +1737,7 @@ namespace AibisDream.MiniGame.Language
initializationFrames = 0;
focusTweensCompleted = false;
interferenceLogicSuspended = false;
ClearCompletionClampSmoothVelocity();
SetStatusUIVisibility(true, 0f);
if (entranceRoutine != null)
{
@@ -14,14 +14,32 @@ namespace AibisDream.MiniGame.Language
private static AibisDream.ExpressionManager ExpressionManager => FixSystemCenter.SystemDic.Get<AibisDream.ExpressionManager>();
/// <summary>
/// 启动表达粒子系统(在打开视图后调用),等待火山表达panel播完再返回
/// 启动表达粒子系统(在打开视图后调用),等待火山表达panel播完再返回
/// 流程:若有上一轮则先淡出字与连线 → 再淡入火山释放 log → 再启动新一轮并等表达 panel 就绪。
/// 注意:Yarn Spinner 2.x 对同名 <see cref="YarnCommand"/> 重载支持不可靠,只保留一个注册入口,用可选参数区分 3/4 参调用。
/// <<start_expression "哈哈|嘿嘿|呵呵" "我很害怕" "ExpressionCompleted">>
/// <<start_expression "哈哈|嘿嘿|呵呵" "我很害怕" "ExpressionCompleted" 24>>
/// (第四项为「非目标」候选粒子总数,候选池 = 目标句字数 + 该值;不写第四项则用 Inspector 的 Candidate Count
/// </summary>
/// <param name="anxietyPhrasesStr">干扰短句(笑话等),用 | 分隔,用于非目标粒子字符池,如:"哈哈|嘿嘿|呵呵"</param>
/// <param name="targetSentence">目标句子</param>
/// <param name="completionNode">完成时触发的对话节点(可</param>
/// <param name="completionNode">完成时触发的对话节点(可空字符串</param>
/// <param name="nonTargetParticleTotal">非目标候选粒子总数;&lt;0 表示使用 Inspector 默认</param>
[YarnCommand("start_expression")]
public static IEnumerator StartExpression(string anxietyPhrasesStr, string targetSentence, string completionNode = "")
public static IEnumerator StartExpression(
string anxietyPhrasesStr,
string targetSentence,
string completionNode = "",
float nonTargetParticleTotal = -1f)
{
return RunStartExpression(
anxietyPhrasesStr,
targetSentence,
completionNode ?? "",
UnityEngine.Mathf.RoundToInt(nonTargetParticleTotal));
}
private static IEnumerator RunStartExpression(string anxietyPhrasesStr, string targetSentence, string completionNode, int nonTargetParticleTotal)
{
if (ExpressionManager == null)
{
@@ -29,7 +47,6 @@ namespace AibisDream.MiniGame.Language
yield break;
}
// 解析干扰短句(用 | 分隔)
string[] phrases = null;
if (!string.IsNullOrEmpty(anxietyPhrasesStr))
{
@@ -40,9 +57,10 @@ namespace AibisDream.MiniGame.Language
}
}
// 若火山释放 log 界面已淡出,先淡入再开始流程
// 先淡出上一轮文字/连线等,再淡入火山释放 log,最后开新一轮粒子(避免 log 与旧字叠在一起)
yield return ExpressionManager.FadeOutParticlesBeforeNewRound(0f);
yield return ExpressionManager.EnsureReleaseLogFadedIn(1f);
ExpressionManager.StartSystem(phrases, targetSentence, completionNode);
ExpressionManager.StartSystem(phrases, targetSentence, completionNode, nonTargetParticleTotal);
yield return ExpressionManager.WaitUntilExpressionFlowReady();
}