diff --git a/Assets/GameContent/Common/NarrativeTextStyleConfig.asset b/Assets/GameContent/Common/NarrativeTextStyleConfig.asset
new file mode 100644
index 000000000..cab9519cb
--- /dev/null
+++ b/Assets/GameContent/Common/NarrativeTextStyleConfig.asset
@@ -0,0 +1,21 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 0df9c4430661480e9c647ea7e22a92b6, type: 3}
+ m_Name: NarrativeTextStyleConfig
+ m_EditorClassIdentifier:
+ _asideColor: {r: 0.37254903, g: 0.37254903, b: 0.37254903, a: 1}
+ _termColor: {r: 0.32156864, g: 0.7137255, b: 1, a: 1}
+ _keyColor: {r: 1, g: 0.69803923, b: 0, a: 1}
+ _commaPauseSeconds: 0.2
+ _ulmEffectPrefix: '[[vertexp d=0.05 bot]]'
+ _jokeOpenTag:
+ _jokeCloseTag:
diff --git a/Assets/GameContent/Common/NarrativeTextStyleConfig.asset.meta b/Assets/GameContent/Common/NarrativeTextStyleConfig.asset.meta
new file mode 100644
index 000000000..c886ed5c2
--- /dev/null
+++ b/Assets/GameContent/Common/NarrativeTextStyleConfig.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b14d9162528749f68d1229a5de940dab
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/AibisDream.Menus/AibisAssetMenus.cs b/Assets/Scripts/AibisDream.Menus/AibisAssetMenus.cs
index 8e291fec3..044cf2672 100644
--- a/Assets/Scripts/AibisDream.Menus/AibisAssetMenus.cs
+++ b/Assets/Scripts/AibisDream.Menus/AibisAssetMenus.cs
@@ -8,6 +8,7 @@ namespace AibisDream
public const string Root = "AIBIS";
public const string NarrativeTalkScene = Root + "/叙事/对话场景";
+ public const string NarrativeTextStyle = Root + "/叙事/文本样式";
public const string FrameAnimationGraph = Root + "/角色动画/帧动画 Graph";
public const string FrameClip = Root + "/角色动画/帧动画 Clip";
public const string BubbleStyle = Root + "/对话 UI/气泡样式";
diff --git a/Assets/Scripts/Dialog System/AibisLineProvider.cs b/Assets/Scripts/Dialog System/AibisLineProvider.cs
index d6c648f66..825e9baef 100644
--- a/Assets/Scripts/Dialog System/AibisLineProvider.cs
+++ b/Assets/Scripts/Dialog System/AibisLineProvider.cs
@@ -29,6 +29,9 @@ namespace AibisDream
[SerializeField, Language]
private string _fallbackLocaleCode = System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
+ [SerializeField]
+ private NarrativeTextStyleConfig _narrativeTextStyle;
+
public string AssetLocaleCode
{
get => _assetLocaleCode;
@@ -82,7 +85,9 @@ namespace AibisDream
line.Substitutions[i] = LocalizationKit.LocalizeParam(line.Substitutions[i]);
}
- // 保留旧 LocalisedLineProvider 的文本转义处理
+ // 先展开项目级语义标记,再保留旧 LocalisedLineProvider 的文本转义处理。
+ // ULM 的 [[vertexp ...]] 会由 Escape 转成 Text Animator 使用的花括号标签。
+ text = NarrativeTextStyleExpander.Expand(text, _narrativeTextStyle);
text = CommonUtil.Escape(text);
var parseResult = lineParser.ParseString(Yarn.Markup.LineParser.ExpandSubstitutions(text, line.Substitutions), LocaleCode);
diff --git a/Assets/Scripts/Dialog System/NarrativeTextStyleConfig.cs b/Assets/Scripts/Dialog System/NarrativeTextStyleConfig.cs
new file mode 100644
index 000000000..6de5f1b78
--- /dev/null
+++ b/Assets/Scripts/Dialog System/NarrativeTextStyleConfig.cs
@@ -0,0 +1,78 @@
+using System.Globalization;
+using UnityEngine;
+
+namespace AibisDream
+{
+ [CreateAssetMenu(
+ fileName = "NarrativeTextStyleConfig",
+ menuName = AibisAssetMenus.NarrativeTextStyle)]
+ public sealed class NarrativeTextStyleConfig : ScriptableObject
+ {
+ [Header("语义颜色")]
+ [SerializeField] private Color _asideColor = new Color32(0x5F, 0x5F, 0x5F, 0xFF);
+ [SerializeField] private Color _termColor = new Color32(0x52, 0xB6, 0xFF, 0xFF);
+ [SerializeField] private Color _keyColor = new Color32(0xFF, 0xB2, 0x00, 0xFF);
+
+ [Header("节奏与效果")]
+ [SerializeField, Min(0f)] private float _commaPauseSeconds = 0.2f;
+ [SerializeField] private string _ulmEffectPrefix = "[[vertexp d=0.05 bot]]";
+ [SerializeField] private string _jokeOpenTag = "";
+ [SerializeField] private string _jokeCloseTag = "";
+
+ public Color AsideColor => _asideColor;
+ public Color TermColor => _termColor;
+ public Color KeyColor => _keyColor;
+ public float CommaPauseSeconds => _commaPauseSeconds;
+ public string UlmEffectPrefix => _ulmEffectPrefix;
+ public string JokeOpenTag => _jokeOpenTag;
+ public string JokeCloseTag => _jokeCloseTag;
+ }
+
+ public static class NarrativeTextStyleExpander
+ {
+ private static readonly Color DefaultAsideColor = new Color32(0x5F, 0x5F, 0x5F, 0xFF);
+ private static readonly Color DefaultTermColor = new Color32(0x52, 0xB6, 0xFF, 0xFF);
+ private static readonly Color DefaultKeyColor = new Color32(0xFF, 0xB2, 0x00, 0xFF);
+
+ public static string Expand(string text, NarrativeTextStyleConfig config)
+ {
+ if (string.IsNullOrEmpty(text))
+ {
+ return text;
+ }
+
+ Color asideColor = config != null ? config.AsideColor : DefaultAsideColor;
+ Color termColor = config != null ? config.TermColor : DefaultTermColor;
+ Color keyColor = config != null ? config.KeyColor : DefaultKeyColor;
+ float commaPause = config != null ? config.CommaPauseSeconds : 0.2f;
+ string ulmPrefix = config != null
+ ? config.UlmEffectPrefix
+ : "[[vertexp d=0.05 bot]]";
+ string jokeOpen = config != null
+ ? config.JokeOpenTag
+ : "";
+ string jokeClose = config != null ? config.JokeCloseTag : "";
+
+ return text
+ .Replace("[[aside]]", ColorOpen(asideColor))
+ .Replace("[[/aside]]", "")
+ .Replace("[[term]]", ColorOpen(termColor))
+ .Replace("[[/term]]", "")
+ .Replace("[[key]]", ColorOpen(keyColor))
+ .Replace("[[/key]]", "")
+ .Replace(
+ "[[comma-pause]]",
+ $"")
+ .Replace("[[ulm]]", ulmPrefix)
+ .Replace("[[joke]]", jokeOpen)
+ .Replace("[[/joke]]", jokeClose);
+ }
+
+ private static string ColorOpen(Color color)
+ {
+ // This runs after Yarn has parsed the source, so the hash must not
+ // carry Yarn's source-level escape into TextMesh Pro.
+ return $"";
+ }
+ }
+}
diff --git a/Assets/Scripts/Dialog System/NarrativeTextStyleConfig.cs.meta b/Assets/Scripts/Dialog System/NarrativeTextStyleConfig.cs.meta
new file mode 100644
index 000000000..427912324
--- /dev/null
+++ b/Assets/Scripts/Dialog System/NarrativeTextStyleConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0df9c4430661480e9c647ea7e22a92b6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: