diff --git a/Assets/Scripts/Dialog System/LineSyncToken.cs b/Assets/Scripts/Dialog System/LineSyncToken.cs
index 749f4d14b..ee3b5f69c 100644
--- a/Assets/Scripts/Dialog System/LineSyncToken.cs
+++ b/Assets/Scripts/Dialog System/LineSyncToken.cs
@@ -128,6 +128,15 @@ namespace AibisDream
public async void TextShown()
{
+ // UI completion events can arrive more than once (for example, when a typewriter is
+ // reused or a previous line finishes late). Only the active PlayText state is allowed
+ // to enter the post-display delay; otherwise a stale callback could regress a token
+ // from Advanced, or run before TextStart and corrupt the line lifecycle.
+ if (state != LineSyncState.PlayText)
+ {
+ return;
+ }
+
AdvanceDelayToken = new CancellationTokenSource();
EnumEventSystem.Global.Send(DialogEventEnum.LineShown);
await _textShown.OnTextShown(this);
diff --git a/Assets/Scripts/FixSystemNew/Screen System/FixPanel/ScreenPanel.cs b/Assets/Scripts/FixSystemNew/Screen System/FixPanel/ScreenPanel.cs
index ef22478a9..897de43cb 100644
--- a/Assets/Scripts/FixSystemNew/Screen System/FixPanel/ScreenPanel.cs
+++ b/Assets/Scripts/FixSystemNew/Screen System/FixPanel/ScreenPanel.cs
@@ -43,6 +43,7 @@ namespace AibisDream.FixSystem
{
EnumEventSystem.Global.Send(ScreenEventEnum.Selected, gameObject);
+ logText.PrepareForLine();
logText.OnTextShown += () => token.TextShown();
bool isWarning = string.Equals(token.lineInfo.character.key, WarningCharacterKey,
@@ -52,8 +53,8 @@ namespace AibisDream.FixSystem
title.color = isWarning ? WarningRedColor : AmberColor;
title.text = token.lineInfo.character.GetActorName();
logText.SetTypewriterSpeed(GetTextSpeedValue());
- logText.AddText(token.lineInfo.lineText);
token.TextStart();
+ logText.AddText(token.lineInfo.lineText);
}
else
{
diff --git a/Assets/Scripts/SceneManagement/SceneCenter/SubwayCenter.cs b/Assets/Scripts/SceneManagement/SceneCenter/SubwayCenter.cs
index 82d39793c..471a43854 100644
--- a/Assets/Scripts/SceneManagement/SceneCenter/SubwayCenter.cs
+++ b/Assets/Scripts/SceneManagement/SceneCenter/SubwayCenter.cs
@@ -205,6 +205,7 @@ namespace AibisDream
}
yield return SubwayCenter.StateMachine.SwitchState(targetState);
+ yield return null;
}
[YarnCommand("arrive_at_station")]
diff --git a/Assets/Scripts/UI/Components/TerminalText.cs b/Assets/Scripts/UI/Components/TerminalText.cs
index a56123cd5..016b8cc1e 100644
--- a/Assets/Scripts/UI/Components/TerminalText.cs
+++ b/Assets/Scripts/UI/Components/TerminalText.cs
@@ -135,6 +135,20 @@ namespace AibisDream.UI
#region 公共方法
+ ///
+ /// 在注册新台词完成回调前停止旧动画,并丢弃可能迟到的旧回调。
+ /// 保留终端中已经显示的历史文本。
+ ///
+ public void PrepareForLine()
+ {
+ if (typewriter != null)
+ {
+ typewriter.StopShowingText();
+ }
+
+ OnTextShown = null;
+ }
+
///
/// 添加新文本(追加到现有文本)
///
diff --git a/Assets/Scripts/UI/DialogUI/Bubbles/Bubble.cs b/Assets/Scripts/UI/DialogUI/Bubbles/Bubble.cs
index a19a40e58..758bcea8c 100644
--- a/Assets/Scripts/UI/DialogUI/Bubbles/Bubble.cs
+++ b/Assets/Scripts/UI/DialogUI/Bubbles/Bubble.cs
@@ -45,6 +45,11 @@ namespace AibisDream
_bubbleGroup = bubbleGroup;
InitRefs();
+ // Dialogue playback is started explicitly by BubbleGroup. OnEnable playback can
+ // resume fully displayed text when a pooled bubble is reactivated and synchronously
+ // emit onTextShowed for the wrong line.
+ _typewriter.startTypewriterMode = TypewriterCore.StartTypewriterMode.OnShowText;
+
role = data.actorRole;
idx = data.idx;
maxWidth = data.maxWidth;
@@ -119,10 +124,16 @@ namespace AibisDream
: characterVo.GetActorName();
}
+ public void PrepareForLine()
+ {
+ _typewriter.StopShowingText();
+ OnLineShown = null;
+ gameObject.SetActive(true);
+ _typewriter.TextAnimator.SetText(string.Empty);
+ }
+
public void ShowLine(string line)
{
- _typewriter.TextAnimator.SetText("");
- gameObject.SetActive(true);
HandleLayout(line);
_typewriter.ShowText(line);
}
@@ -215,9 +226,10 @@ namespace AibisDream
public int Idx { get; }
public event Action OnLineShown;
+ public void PrepareForLine();
public void ShowActorName(CharacterVo characterVo);
public void ShowLine(string line);
public bool SkipLine();
public void Hide();
}
-}
\ No newline at end of file
+}
diff --git a/Assets/Scripts/UI/DialogUI/Bubbles/BubbleGroup.cs b/Assets/Scripts/UI/DialogUI/Bubbles/BubbleGroup.cs
index 2857b0918..803c62f27 100644
--- a/Assets/Scripts/UI/DialogUI/Bubbles/BubbleGroup.cs
+++ b/Assets/Scripts/UI/DialogUI/Bubbles/BubbleGroup.cs
@@ -73,14 +73,19 @@ namespace AibisDream
if (TryGetBubble(character.role, character.bubbleIdx, out var bubble))
{
+ // Stop the previous typewriter and remove its completion callback before the new
+ // token is registered. A forcibly advanced line may otherwise finish late and
+ // complete the next token.
+ bubble.PrepareForLine();
+
// 注册事件
bubble.OnLineShown += syncToken.TextShown;
syncToken.SkipTextAnima += () => bubble.SkipLine();
syncToken.OnAdvance += AfterAdvance;
// 播放文字
bubble.ShowActorName(character);
- bubble.ShowLine(syncToken.lineInfo.lineText);
syncToken.TextStart();
+ bubble.ShowLine(syncToken.lineInfo.lineText);
// 将非选定的Bubble隐藏
HideDialog(bubble);
}
@@ -138,4 +143,4 @@ namespace AibisDream
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/Assets/Scripts/UI/DialogUI/CenterText.cs b/Assets/Scripts/UI/DialogUI/CenterText.cs
index 612f4b1e3..b2653ac09 100644
--- a/Assets/Scripts/UI/DialogUI/CenterText.cs
+++ b/Assets/Scripts/UI/DialogUI/CenterText.cs
@@ -24,6 +24,13 @@ namespace AibisDream
// 先中断结束事件
_autoHideToken?.Cancel();
_autoHideToken = null;
+ OnTextShowed = null;
+
+ // 先停止旧动画并准备好激活状态,避免 OnEnable 或旧动画完成事件命中新 token。
+ content.StopShowingText();
+ box.SetActive(true);
+ content.StopShowingText();
+ content.TextAnimator.SetText(string.Empty);
// 注册事件
OnTextShowed += syncToken.TextShown;
@@ -31,10 +38,8 @@ namespace AibisDream
syncToken.OnAdvance += AfterAdvance;
// 播放文字
- content.TextAnimator.SetText(string.Empty);
- box.SetActive(true);
- content.ShowText(syncToken.lineInfo.lineText);
syncToken.TextStart();
+ content.ShowText(syncToken.lineInfo.lineText);
}
private async void AfterAdvance()
@@ -109,4 +114,4 @@ namespace AibisDream
HideDialog();
}
}
-}
\ No newline at end of file
+}