feat: 硬编码本地化
This commit is contained in:
+12
@@ -23,6 +23,18 @@ MonoBehaviour:
|
||||
m_SerializedLabels:
|
||||
- Locale-pt-BR
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: 6ad3c9298dbc3b642b1251ffc306eaa6
|
||||
m_Address: Params_pt-BR
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-pt-BR
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: 7d496f91e240bb143b62a6239c817d09
|
||||
m_Address: UIText_pt-BR
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-pt-BR
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 1
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
|
||||
+12
@@ -23,6 +23,18 @@ MonoBehaviour:
|
||||
m_SerializedLabels:
|
||||
- Locale-ru
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: b91f4281442d2b54f92fbecb5bd56426
|
||||
m_Address: Params_ru
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-ru
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: f679069e455200841a92c27186383940
|
||||
m_Address: UIText_ru
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-ru
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 1
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
|
||||
+12
@@ -23,6 +23,18 @@ MonoBehaviour:
|
||||
m_SerializedLabels:
|
||||
- Locale-es
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: 1edf9254b97981643bdecd6d22ed1150
|
||||
m_Address: Params_es
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-es
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
- m_GUID: 380c27123212d66468aa1f3ed3bf72ea
|
||||
m_Address: UIText_es
|
||||
m_ReadOnly: 1
|
||||
m_SerializedLabels:
|
||||
- Locale-es
|
||||
FlaggedDuringContentUpdateRestriction: 0
|
||||
m_ReadOnly: 1
|
||||
m_Settings: {fileID: 11400000, guid: 77169ce22e430f64fb36c771815a4a7b, type: 2}
|
||||
m_SchemaSet:
|
||||
|
||||
@@ -41,6 +41,26 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
Assert.That(LocalizationKit.GetL10NParamKey("raw"), Is.EqualTo("raw"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MissingParamResult_RequiresMatchingLocalizedReference()
|
||||
{
|
||||
Assert.That(
|
||||
LocalizationKit.IsMissingParamResult(
|
||||
"l10n.hs.exp.pool",
|
||||
"⟦hs.exp.pool⟧"),
|
||||
Is.True);
|
||||
Assert.That(
|
||||
LocalizationKit.IsMissingParamResult(
|
||||
"l10n.hs.exp.pool",
|
||||
"⟦another.key⟧"),
|
||||
Is.False);
|
||||
Assert.That(
|
||||
LocalizationKit.IsMissingParamResult(
|
||||
"raw",
|
||||
"⟦raw⟧"),
|
||||
Is.False);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator LocalizeParamAsync_UsesExplicitLocaleAndDoesNotFallback()
|
||||
{
|
||||
@@ -111,7 +131,6 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionContentCatalog>(CatalogPath);
|
||||
Assert.That(catalog, Is.Not.Null);
|
||||
Assert.That(catalog.GetValidationErrors(), Is.Empty);
|
||||
Assert.That(catalog.ActorLoopReference, Is.EqualTo("l10n.hs.exp.actor.loop"));
|
||||
Assert.That(catalog.Rounds.Select(round => round.Id),
|
||||
Is.EqualTo(new[] { "log1", "log2", "log3" }));
|
||||
|
||||
@@ -128,7 +147,6 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
ScriptableObject.CreateInstance<ExpressionContentCatalog>();
|
||||
try
|
||||
{
|
||||
SetPrivateField(catalog, "actorLoopReference", "raw");
|
||||
var first = new ExpressionRoundDefinition();
|
||||
var second = new ExpressionRoundDefinition();
|
||||
ConfigureRound(first, "log1", "l10n.target", "l10n.tokens", 1);
|
||||
@@ -139,7 +157,6 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
new List<ExpressionRoundDefinition> { first, second });
|
||||
|
||||
string errors = string.Join("\n", catalog.GetValidationErrors());
|
||||
Assert.That(errors, Does.Contain("Actor Loop"));
|
||||
Assert.That(errors, Does.Contain("重复"));
|
||||
Assert.That(errors, Does.Contain("l10n.*"));
|
||||
}
|
||||
@@ -168,6 +185,100 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CharacterPoolParser_PreservesUnicodeDuplicatesAndRejectsSeparators()
|
||||
{
|
||||
string combining = "e\u0301";
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(
|
||||
$" 中 中 あ {combining} 😀。 ",
|
||||
out List<string> pool),
|
||||
Is.True);
|
||||
Assert.That(
|
||||
pool,
|
||||
Is.EqualTo(new[] { "中", "中", "あ", combining, "😀", "。" }));
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool("中|あ", out _),
|
||||
Is.False);
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool("中|あ", out _),
|
||||
Is.False);
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(" \t ", out _),
|
||||
Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Params_ContainsLocalizedExpressPoolForEveryLocale()
|
||||
{
|
||||
StringTableCollection collection =
|
||||
LocalizationEditorSettings.GetStringTableCollection(ConstRef.ParamsTable);
|
||||
Assert.That(collection, Is.Not.Null);
|
||||
|
||||
string key =
|
||||
LocalizationKit.GetL10NParamKey(ConstRef.ExpressParticlePoolParam);
|
||||
Assert.That(collection.SharedData.GetEntry(key), Is.Not.Null);
|
||||
|
||||
string[] localeCodes = { "zh-Hans", "en", "ja-JP", "es", "ru", "pt-BR" };
|
||||
foreach (string localeCode in localeCodes)
|
||||
{
|
||||
StringTable table =
|
||||
collection.GetTable(new LocaleIdentifier(localeCode)) as StringTable;
|
||||
Assert.That(table, Is.Not.Null, localeCode);
|
||||
Assert.That(table.GetEntry(key), Is.Not.Null, localeCode);
|
||||
}
|
||||
|
||||
StringTable chinese =
|
||||
collection.GetTable(new LocaleIdentifier("zh-Hans")) as StringTable;
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.GetVisibleElements(
|
||||
chinese.GetEntry(key).LocalizedValue),
|
||||
Is.Not.Empty);
|
||||
|
||||
foreach (string localeCode in localeCodes.Skip(1))
|
||||
{
|
||||
StringTable table =
|
||||
collection.GetTable(new LocaleIdentifier(localeCode)) as StringTable;
|
||||
Assert.That(table.GetEntry(key).LocalizedValue, Is.Empty, localeCode);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextParticle_UsesConfiguredRoleAndOverridePoolsWithoutFirstFrameFallback()
|
||||
{
|
||||
var gameObject = new GameObject("TextParticlePoolTest");
|
||||
try
|
||||
{
|
||||
CandidateParticle particle = gameObject.AddComponent<CandidateParticle>();
|
||||
Assert.That(particle.currentChar, Is.Empty);
|
||||
|
||||
string combining = "e\u0301";
|
||||
particle.SetCharacterPools(
|
||||
new[] { "😀" },
|
||||
new[] { combining });
|
||||
|
||||
particle.originalIsRed = true;
|
||||
particle.InitializeRandomCharacter();
|
||||
Assert.That(particle.currentChar, Is.EqualTo("😀"));
|
||||
|
||||
particle.originalIsRed = false;
|
||||
particle.InitializeRandomCharacter();
|
||||
Assert.That(particle.currentChar, Is.EqualTo(combining));
|
||||
|
||||
particle.SetOverrideCharacterPool(new[] { "。" });
|
||||
particle.InitializeRandomCharacter();
|
||||
Assert.That(particle.currentChar, Is.EqualTo("。"));
|
||||
|
||||
particle.SetOverrideCharacterPool(null);
|
||||
particle.InitializeRandomCharacter();
|
||||
Assert.That(particle.currentChar, Is.EqualTo(combining));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnicodeTokenizer_PreservesTextElementsAndDropsWhitespace()
|
||||
{
|
||||
@@ -221,7 +332,7 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
issues.Any(issue =>
|
||||
issue.Severity == ExpressionLocalizationIssueSeverity.Warning),
|
||||
Is.True,
|
||||
"英日空条目应作为待翻译警告保留。");
|
||||
"未翻译 Locale 的空条目应作为待翻译警告保留。");
|
||||
}
|
||||
|
||||
private static void ConfigureRound(
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
|
||||
/// <summary>
|
||||
/// 校验 Stage6 Express 命令、轮次 Catalog 与 Params String Table 的引用关系。
|
||||
/// 英日空值是待翻译警告,不阻断内容校验。
|
||||
/// 未翻译 Locale 的空值是待翻译警告,不阻断内容校验。
|
||||
/// </summary>
|
||||
public static class ExpressionLocalizationValidator
|
||||
{
|
||||
@@ -60,7 +60,6 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
new(StringComparer.Ordinal)
|
||||
{
|
||||
["start_expression"] = new[] { 0, 1 },
|
||||
["expression_lie_begin"] = new[] { 0 },
|
||||
["expression_truth_lie_flicker"] = new[] { 0, 1 },
|
||||
["expression_lie_morph_truth"] = new[] { 0, 1 },
|
||||
["expression_truth_leak"] = new[] { 0 },
|
||||
@@ -109,6 +108,8 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
|
||||
ValidateStage6(issues, referencedKeys);
|
||||
ValidateCatalog(issues, referencedKeys);
|
||||
referencedKeys.Add(
|
||||
LocalizationKit.GetL10NParamKey(ConstRef.ExpressParticlePoolParam));
|
||||
ValidateParams(issues, referencedKeys);
|
||||
return issues;
|
||||
}
|
||||
@@ -178,7 +179,6 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
foreach (string validationError in catalog.GetValidationErrors())
|
||||
AddError(issues, validationError);
|
||||
|
||||
AddCatalogReference(catalog.ActorLoopReference, referencedKeys);
|
||||
foreach (ExpressionRoundDefinition round in catalog.Rounds)
|
||||
{
|
||||
if (round == null)
|
||||
@@ -206,6 +206,14 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
collection.GetTable(new LocaleIdentifier("en")) as StringTable;
|
||||
StringTable japanese =
|
||||
collection.GetTable(new LocaleIdentifier("ja-JP")) as StringTable;
|
||||
StringTable spanish =
|
||||
collection.GetTable(new LocaleIdentifier("es")) as StringTable;
|
||||
StringTable russian =
|
||||
collection.GetTable(new LocaleIdentifier("ru")) as StringTable;
|
||||
StringTable portuguese =
|
||||
collection.GetTable(new LocaleIdentifier("pt-BR")) as StringTable;
|
||||
string poolKey =
|
||||
LocalizationKit.GetL10NParamKey(ConstRef.ExpressParticlePoolParam);
|
||||
|
||||
foreach (string key in referencedKeys.OrderBy(value => value, StringComparer.Ordinal))
|
||||
{
|
||||
@@ -222,14 +230,51 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.EndsWith(".tokens", StringComparison.Ordinal) ||
|
||||
string.Equals(key, "hs.exp.actor.loop", StringComparison.Ordinal))
|
||||
if (key.EndsWith(".tokens", StringComparison.Ordinal))
|
||||
{
|
||||
ValidatePipeList(issues, key, chineseEntry.LocalizedValue);
|
||||
}
|
||||
|
||||
AddPendingTranslation(issues, english, "en", key);
|
||||
AddPendingTranslation(issues, japanese, "ja-JP", key);
|
||||
if (string.Equals(key, poolKey, StringComparison.Ordinal))
|
||||
{
|
||||
ValidateCharacterPool(
|
||||
issues,
|
||||
key,
|
||||
"zh-Hans",
|
||||
chineseEntry.LocalizedValue);
|
||||
AddPendingPoolTranslation(issues, english, "en", key);
|
||||
AddPendingPoolTranslation(issues, japanese, "ja-JP", key);
|
||||
AddPendingPoolTranslation(issues, spanish, "es", key);
|
||||
AddPendingPoolTranslation(issues, russian, "ru", key);
|
||||
AddPendingPoolTranslation(issues, portuguese, "pt-BR", key);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddPendingTranslation(issues, english, "en", key);
|
||||
AddPendingTranslation(issues, japanese, "ja-JP", key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateCharacterPool(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
string key,
|
||||
string localeCode,
|
||||
string value)
|
||||
{
|
||||
if (value.Contains('|') || value.Contains('|'))
|
||||
{
|
||||
AddError(
|
||||
issues,
|
||||
$"Params/{key}, Locale={localeCode} 的字符池不能包含“|”或“|”。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LanguageYarnCommand.TryParseExpressionPool(value, out _))
|
||||
{
|
||||
AddError(
|
||||
issues,
|
||||
$"Params/{key}, Locale={localeCode} 没有有效的 Unicode 文本元素。");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,6 +317,34 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddPendingPoolTranslation(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
StringTable table,
|
||||
string localeCode,
|
||||
string key)
|
||||
{
|
||||
StringTableEntry entry = table?.GetEntry(key);
|
||||
if (entry == null)
|
||||
{
|
||||
AddError(issues, $"Params/{key} 缺少 {localeCode} 条目。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(entry.LocalizedValue))
|
||||
{
|
||||
issues.Add(new ExpressionLocalizationIssue(
|
||||
ExpressionLocalizationIssueSeverity.Warning,
|
||||
$"待翻译:Params/{key}, Locale={localeCode}"));
|
||||
return;
|
||||
}
|
||||
|
||||
ValidateCharacterPool(
|
||||
issues,
|
||||
key,
|
||||
localeCode,
|
||||
entry.LocalizedValue);
|
||||
}
|
||||
|
||||
private static void AddCatalogReference(
|
||||
string reference,
|
||||
HashSet<string> referencedKeys)
|
||||
|
||||
@@ -105,17 +105,12 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
Assert.That(lastCompletion, Is.EqualTo(totalDuration).Within(0.0001f));
|
||||
}
|
||||
|
||||
[TestCase("LightPreset", 0.10f, 0.35f, 0.25f, 0.25f, 0.70f, 0.15f)]
|
||||
[TestCase("MediumPreset", 0.20f, 0.55f, 0.50f, 0.35f, 0.90f, 0.30f)]
|
||||
[TestCase("HeavyPreset", 0.35f, 0.80f, 0.85f, 0.50f, 1.10f, 0.50f)]
|
||||
[TestCase("LightPreset", 0.10f)]
|
||||
[TestCase("MediumPreset", 0.20f)]
|
||||
[TestCase("HeavyPreset", 0.35f)]
|
||||
public void PresentationPreset_UsesApprovedValues(
|
||||
string presetFieldName,
|
||||
float baseDistortion,
|
||||
float liePause,
|
||||
float breakPeak,
|
||||
float breakDuration,
|
||||
float truthDuration,
|
||||
float truthPeripheral)
|
||||
float baseDistortion)
|
||||
{
|
||||
const BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic;
|
||||
FieldInfo presetField = typeof(LogReleasePresentationController).GetField(presetFieldName, flags);
|
||||
@@ -124,11 +119,6 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
Type presetType = preset.GetType();
|
||||
|
||||
AssertPresetField(presetType, preset, "BaseDistortion", baseDistortion);
|
||||
AssertPresetField(presetType, preset, "LiePause", liePause);
|
||||
AssertPresetField(presetType, preset, "BreakPeak", breakPeak);
|
||||
AssertPresetField(presetType, preset, "BreakDuration", breakDuration);
|
||||
AssertPresetField(presetType, preset, "TruthDuration", truthDuration);
|
||||
AssertPresetField(presetType, preset, "TruthPeripheral", truthPeripheral);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
Binary file not shown.
@@ -12,7 +12,6 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: a86a72005fba4c99a59962fb9e92df83, type: 3}
|
||||
m_Name: ExpressionContentCatalog
|
||||
m_EditorClassIdentifier:
|
||||
actorLoopReference: l10n.hs.exp.actor.loop
|
||||
rounds:
|
||||
- id: log1
|
||||
targetReference: l10n.hs.exp.log1.target
|
||||
|
||||
@@ -67,10 +67,6 @@ MonoBehaviour:
|
||||
m_Key: hs.exp.log3.tokens
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001007
|
||||
m_Key: hs.exp.actor.loop
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001008
|
||||
m_Key: hs.exp.atk.why
|
||||
m_Metadata:
|
||||
@@ -155,6 +151,10 @@ MonoBehaviour:
|
||||
m_Key: hs.exp.final.win
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001029
|
||||
m_Key: hs.exp.pool
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
m_KeyGenerator:
|
||||
|
||||
@@ -17,6 +17,9 @@ MonoBehaviour:
|
||||
- {fileID: 11400000, guid: 9248cb179e7aafa4c903f1fc54b41ef7, type: 2}
|
||||
- {fileID: 11400000, guid: aeb318c895d57014b89d5540c6baff95, type: 2}
|
||||
- {fileID: 11400000, guid: 9ed23d1d910a1a147ba5c316a87cbad4, type: 2}
|
||||
- {fileID: 11400000, guid: 6ad3c9298dbc3b642b1251ffc306eaa6, type: 2}
|
||||
- {fileID: 11400000, guid: b91f4281442d2b54f92fbecb5bd56426, type: 2}
|
||||
- {fileID: 11400000, guid: 1edf9254b97981643bdecd6d22ed1150, type: 2}
|
||||
m_Extensions: []
|
||||
m_Group: String Table
|
||||
references:
|
||||
|
||||
@@ -70,10 +70,6 @@ MonoBehaviour:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001007
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001008
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
@@ -158,6 +154,10 @@ MonoBehaviour:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001029
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds: []
|
||||
|
||||
@@ -17,7 +17,11 @@ MonoBehaviour:
|
||||
m_SharedData: {fileID: 11400000, guid: 4a2d391471bd9cd439150c52659e9846, type: 2}
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
m_TableData: []
|
||||
m_TableData:
|
||||
- m_Id: 200000000000001029
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds: []
|
||||
|
||||
@@ -70,10 +70,6 @@ MonoBehaviour:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001007
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001008
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
@@ -158,6 +154,10 @@ MonoBehaviour:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001029
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds: []
|
||||
|
||||
@@ -17,7 +17,11 @@ MonoBehaviour:
|
||||
m_SharedData: {fileID: 11400000, guid: 4a2d391471bd9cd439150c52659e9846, type: 2}
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
m_TableData: []
|
||||
m_TableData:
|
||||
- m_Id: 200000000000001029
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds: []
|
||||
|
||||
@@ -17,7 +17,11 @@ MonoBehaviour:
|
||||
m_SharedData: {fileID: 11400000, guid: 4a2d391471bd9cd439150c52659e9846, type: 2}
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
m_TableData: []
|
||||
m_TableData:
|
||||
- m_Id: 200000000000001029
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds: []
|
||||
|
||||
@@ -70,10 +70,6 @@ MonoBehaviour:
|
||||
m_Localized: "\u54C8\u54C8\u54C8\u54C8|\u7B11\u6B7B\u6211\u4E86|\u592A\u597D\u7B11\u4E86|\u7EF7\u4E0D\u4F4F\u4E86|\u7B11\u4E0D\u6D3B\u4E86|\u771F\u7684\u592A\u641E\u7B11"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001007
|
||||
m_Localized: "\u6CA1\u95EE\u9898|\u5187\u95EE\u9898|\u5E3D\u95EE\u9898"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001008
|
||||
m_Localized: "\u4E3A\u4EC0\u4E48"
|
||||
m_Metadata:
|
||||
@@ -158,6 +154,10 @@ MonoBehaviour:
|
||||
m_Localized: "\u6211\u60F3\u8D62\u4E00\u6B21"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000001029
|
||||
m_Localized: 的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进着等部度家电力里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其些然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取据处队南给色光门即保治北造百规热领七海口东导器压志世金增争济阶油思术极交受联什认六共权收证改清己美再采转更单风切打白教速花带安场身车例真务具万每目至达走积示议声报斗完类八离华名确才科张信马节话米整空元况今集温传土许步群广石记需段研界拉林律叫且究观越织装影算低持音众书布复容儿须际商非验连断深难近矿千周委素技备半办青省列习响约支般史感劳便团往酸历市克何除消构府称太准精值号率族维划选标写存候毛亲快效斯院查江型眼王按格养易置派层片始却专状育厂京识适属圆包火住调满县局照参红细引听该铁价严龙飞
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds: []
|
||||
|
||||
@@ -19,10 +19,6 @@ MonoBehaviour:
|
||||
m_Key: start_newGame
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296654778368
|
||||
m_Key: start_title
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296671555584
|
||||
m_Key: start_selcetLevel
|
||||
m_Metadata:
|
||||
@@ -31,38 +27,10 @@ MonoBehaviour:
|
||||
m_Key: start_quit
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749889
|
||||
m_Key: start_survey
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749890
|
||||
m_Key: community
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749891
|
||||
m_Key: record_title
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 26730768109568
|
||||
m_Key: record_continue
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749892
|
||||
m_Key: main_setting
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749893
|
||||
m_Key: main_record
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749894
|
||||
m_Key: main_auto
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749895
|
||||
m_Key: main_fast
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749896
|
||||
m_Key: setting_title
|
||||
m_Metadata:
|
||||
@@ -119,10 +87,6 @@ MonoBehaviour:
|
||||
m_Key: setting_textSpeed_fast
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 752706112196608
|
||||
m_Key: save_warning
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 753317281009664
|
||||
m_Key: save_return
|
||||
m_Metadata:
|
||||
|
||||
@@ -17,6 +17,9 @@ MonoBehaviour:
|
||||
- {fileID: 11400000, guid: f3e7269fc46911b4dbe7ea36aa77aa90, type: 2}
|
||||
- {fileID: 11400000, guid: e6ecffb64cf71b44bbc44ebf958029cc, type: 2}
|
||||
- {fileID: 11400000, guid: 7c757424270c91e43bf45198f69c983a, type: 2}
|
||||
- {fileID: 11400000, guid: 7d496f91e240bb143b62a6239c817d09, type: 2}
|
||||
- {fileID: 11400000, guid: f679069e455200841a92c27186383940, type: 2}
|
||||
- {fileID: 11400000, guid: 380c27123212d66468aa1f3ed3bf72ea, type: 2}
|
||||
m_Extensions: []
|
||||
m_Group: String Table
|
||||
references:
|
||||
|
||||
@@ -16,18 +16,12 @@ MonoBehaviour:
|
||||
m_Code: en
|
||||
m_SharedData: {fileID: 11400000, guid: 994c69bf4b2403043bc9562f2d24ee56, type: 2}
|
||||
m_Metadata:
|
||||
m_Items:
|
||||
- rid: 7917695930343620616
|
||||
m_Items: []
|
||||
m_TableData:
|
||||
- m_Id: 3350325104640
|
||||
m_Localized: New Game
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296654778368
|
||||
m_Localized: All Our\nBroken Parts
|
||||
m_Metadata:
|
||||
m_Items:
|
||||
- rid: 7917695930343620616
|
||||
- m_Id: 22296671555584
|
||||
m_Localized: Chapter Select
|
||||
m_Metadata:
|
||||
@@ -40,34 +34,6 @@ MonoBehaviour:
|
||||
m_Localized: Exit
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749889
|
||||
m_Localized: Questionnaire
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749890
|
||||
m_Localized: Discord
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749891
|
||||
m_Localized: Medical Records
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749892
|
||||
m_Localized: Settings
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749893
|
||||
m_Localized: Log
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749894
|
||||
m_Localized: Auto
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749895
|
||||
m_Localized: Fast Forward
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749896
|
||||
m_Localized: <SETTINGS>
|
||||
m_Metadata:
|
||||
@@ -128,19 +94,6 @@ MonoBehaviour:
|
||||
m_Localized: Fast
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 752706112196608
|
||||
m_Localized: 'This is a temporary feature prepared for testing purposes.
|
||||
|
||||
|
||||
If
|
||||
this is your first playthrough, we recommend going back and clicking Start
|
||||
Game to begin.
|
||||
|
||||
|
||||
If you wish to select a chapter, please restart the
|
||||
game before using this feature, as bugs may otherwise occur.'
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 753317281009664
|
||||
m_Localized: Back
|
||||
m_Metadata:
|
||||
@@ -258,25 +211,21 @@ MonoBehaviour:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000000001
|
||||
m_Localized:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000000002
|
||||
m_Localized:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000000003
|
||||
m_Localized:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000000004
|
||||
m_Localized:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 7917695930343620616
|
||||
type: {class: SmartFormatTag, ns: UnityEngine.Localization.Metadata, asm: Unity.Localization}
|
||||
data:
|
||||
m_Entries: 0010305847140000
|
||||
RefIds: []
|
||||
|
||||
@@ -22,10 +22,6 @@ MonoBehaviour:
|
||||
m_Localized: "\u30CB\u30E5\u30FC\u30B2\u30FC\u30E0"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296654778368
|
||||
m_Localized: "\u611B\u3068\u30ED\u30DC\u30C3\u30C8\n\u4FEE\u7406\u6280\u8853"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296671555584
|
||||
m_Localized: "\u30C1\u30E3\u30D7\u30BF\u30FC\u9078\u629E"
|
||||
m_Metadata:
|
||||
@@ -38,38 +34,10 @@ MonoBehaviour:
|
||||
m_Localized: "\u7D42\u4E86"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749889
|
||||
m_Localized: "\u30A2\u30F3\u30B1\u30FC\u30C8"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749890
|
||||
m_Localized: Discord
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749891
|
||||
m_Localized: "\u30AB\u30EB\u30C6"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 26730768109568
|
||||
m_Localized: "\u623B\u308B"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749892
|
||||
m_Localized: "\u8A2D\u5B9A"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749893
|
||||
m_Localized: "\u30D0\u30C3\u30AF\u30ED\u30B0"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749894
|
||||
m_Localized: "\u81EA\u52D5"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749895
|
||||
m_Localized: "\u65E9\u9001\u308A"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749896
|
||||
m_Localized: "<\u8A2D\u5B9A>"
|
||||
m_Metadata:
|
||||
@@ -126,10 +94,6 @@ MonoBehaviour:
|
||||
m_Localized: "\u9AD8\u901F"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 752706112196608
|
||||
m_Localized: "\u3053\u308C\u306F\u30C6\u30B9\u30C8\u7528\u306E\u4E00\u6642\u7684\u306A\u6A5F\u80FD\u3067\u3059\n\n\u521D\u3081\u3066\u30D7\u30EC\u30A4\u3059\u308B\u5834\u5408\u306F\u3001\u623B\u3063\u3066\u300C\u30B2\u30FC\u30E0\u30B9\u30BF\u30FC\u30C8\u300D\u304B\u3089\u59CB\u3081\u308B\u3053\u3068\u3092\u304A\u52E7\u3081\u3057\u307E\u3059\n\n\u30C1\u30E3\u30D7\u30BF\u30FC\u9078\u629E\u3092\u4F7F\u7528\u3057\u305F\u3044\u65B9\u306F\u3001\u30B2\u30FC\u30E0\u3092\u518D\u8D77\u52D5\u3057\u3066\u304B\u3089\u3054\u5229\u7528\u304F\u3060\u3055\u3044\u3002\u30D0\u30B0\u304C\u767A\u751F\u3059\u308B\u6050\u308C\u304C\u3042\u308A\u307E\u3059"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 753317281009664
|
||||
m_Localized: "\u623B\u308B"
|
||||
m_Metadata:
|
||||
@@ -247,19 +211,19 @@ MonoBehaviour:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000000001
|
||||
m_Localized:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000000002
|
||||
m_Localized:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000000003
|
||||
m_Localized:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 200000000000000004
|
||||
m_Localized:
|
||||
m_Localized:
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
references:
|
||||
|
||||
@@ -16,18 +16,12 @@ MonoBehaviour:
|
||||
m_Code: zh-Hans
|
||||
m_SharedData: {fileID: 11400000, guid: 994c69bf4b2403043bc9562f2d24ee56, type: 2}
|
||||
m_Metadata:
|
||||
m_Items:
|
||||
- rid: 7917695939544875419
|
||||
m_Items: []
|
||||
m_TableData:
|
||||
- m_Id: 3350325104640
|
||||
m_Localized: "\u65B0\u6E38\u620F"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296654778368
|
||||
m_Localized: "\u7231\u4E0E\u673A\u5668\u4EBA\\n\u7EF4\u4FEE\u6280\u672F"
|
||||
m_Metadata:
|
||||
m_Items:
|
||||
- rid: 7917695939544875419
|
||||
- m_Id: 22296671555584
|
||||
m_Localized: "\u9009\u62E9\u7AE0\u8282"
|
||||
m_Metadata:
|
||||
@@ -40,38 +34,10 @@ MonoBehaviour:
|
||||
m_Localized: "\u9000\u51FA"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749889
|
||||
m_Localized: "\u95EE\u5377"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749890
|
||||
m_Localized: 325268983
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749891
|
||||
m_Localized: "\u8BCA\u7597\u8BB0\u5F55"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 26730768109568
|
||||
m_Localized: "\u8FD4\u56DE"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749892
|
||||
m_Localized: "\u8BBE\u7F6E"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749893
|
||||
m_Localized: "\u8BB0\u5F55"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749894
|
||||
m_Localized: "\u81EA\u52A8"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749895
|
||||
m_Localized: "\u5FEB\u8FDB"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 22296675749896
|
||||
m_Localized: "<\u8BBE\u7F6E>"
|
||||
m_Metadata:
|
||||
@@ -128,12 +94,6 @@ MonoBehaviour:
|
||||
m_Localized: "\u5FEB\u901F"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 752706112196608
|
||||
m_Localized: "\u8FD9\u662F\u4E00\u4E2A\u4E3A\u4FBF\u4E8E\u6D4B\u8BD5\u51C6\u5907\u7684\u4E34\u65F6\u529F\u80FD\n
|
||||
\n \u5982\u679C\u9996\u6B21\u6E38\u73A9\uFF0C\u63A8\u8350\u60A8\u8FD4\u56DE\u5E76\u70B9\u51FB\u5F00\u59CB\u6E38\u620F\u76F4\u63A5\u5F00\u59CB\n
|
||||
\n \u63A8\u8350\u60F3\u8981\u9009\u5173\u7684\u73A9\u5BB6\u91CD\u542F\u6E38\u620F\u540E\u518D\u4F7F\u7528\u672C\u529F\u80FD,\u5426\u5219\u53EF\u80FD\u51FA\u73B0bug"
|
||||
m_Metadata:
|
||||
m_Items: []
|
||||
- m_Id: 753317281009664
|
||||
m_Localized: "\u8FD4\u56DE"
|
||||
m_Metadata:
|
||||
@@ -268,8 +228,4 @@ MonoBehaviour:
|
||||
m_Items: []
|
||||
references:
|
||||
version: 2
|
||||
RefIds:
|
||||
- rid: 7917695939544875419
|
||||
type: {class: SmartFormatTag, ns: UnityEngine.Localization.Metadata, asm: Unity.Localization}
|
||||
data:
|
||||
m_Entries: 0010305847140000
|
||||
RefIds: []
|
||||
|
||||
@@ -173,8 +173,6 @@ MonoBehaviour:
|
||||
redParticleCount: 8
|
||||
connectionDistance: 2
|
||||
textMargin: 1
|
||||
anxietyPhrases: []
|
||||
targetSentence: "\u522B\u8FC7\u6765\u6211\u611F\u89C9\u5BB3\u6015"
|
||||
completionDialogNode:
|
||||
waveformAmplitude: 0.3
|
||||
interactionRadius: 0.5
|
||||
|
||||
@@ -16128,13 +16128,6 @@ MonoBehaviour:
|
||||
logReleasePresentation: {fileID: 2138889543}
|
||||
screenPresentation: {fileID: 2138889544}
|
||||
contentCatalog: {fileID: 11400000, guid: b2d94952a18d4d178421691ab103f0fd, type: 2}
|
||||
defaultAnxietyPhrases:
|
||||
- "\u6211\u597D\u6015"
|
||||
- "\u600E\u4E48\u529E"
|
||||
- "\u4E0D\u884C"
|
||||
- "\u597D\u96BE"
|
||||
- "\u505A\u4E0D\u5230"
|
||||
defaultTargetSentence: "\u8FD9\u662F\u4E00\u4E2A\u6D4B\u8BD5\u793A\u4F8B"
|
||||
screenOverlayRenderer: {fileID: 2063129674}
|
||||
volcanoOverlayRenderer: {fileID: 1944146357}
|
||||
spriteOverlayFadeInAlpha: 1
|
||||
@@ -16218,7 +16211,6 @@ MonoBehaviour:
|
||||
memoryExitDuration: 0.45
|
||||
faceDipRatio: 0.015
|
||||
lieColor: {r: 0.66, g: 0.94, b: 1, a: 1}
|
||||
errorColor: {r: 1, g: 0.34, b: 0.18, a: 1}
|
||||
truthWarmColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
truthAccentColor: {r: 1, g: 0.84, b: 0.6, a: 1}
|
||||
truthBackdropColor: {r: 0.3, g: 0.2, b: 0.1, a: 0.78}
|
||||
@@ -18471,8 +18463,6 @@ MonoBehaviour:
|
||||
redParticleCount: 8
|
||||
connectionDistance: 1.8
|
||||
textMargin: 0.45
|
||||
anxietyPhrases: []
|
||||
targetSentence: "\u522B\u8FC7\u6765\u6211\u611F\u89C9\u5BB3\u6015"
|
||||
completionDialogNode:
|
||||
waveformAmplitude: 0.3
|
||||
interactionRadius: 0.5
|
||||
@@ -18576,7 +18566,7 @@ MonoBehaviour:
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: "\u8F93\u51FA"
|
||||
m_text:
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: f242fb3dde1933640859f1c54591c9c8, type: 2}
|
||||
m_sharedMaterial: {fileID: -6659093157667844055, guid: f242fb3dde1933640859f1c54591c9c8, type: 2}
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace AibisDream.Utility
|
||||
public const string UITextTable = "UIText";
|
||||
public const string ChapterInfoTable = "ChapterInfo";
|
||||
public const string ParamsTable = "Params";
|
||||
public const string ExpressParticlePoolParam = "l10n.hs.exp.pool";
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -210,6 +210,22 @@ namespace AibisDream.Framework
|
||||
return IsLocalizedParam(key) ? key[LocalizationPrefix.Length..] : key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断解析结果是否是当前 <c>l10n.</c> 引用对应的缺失标记。
|
||||
/// 非本地化原始文本即使使用相同括号格式也不会被误判。
|
||||
/// </summary>
|
||||
public static bool IsMissingParamResult(string source, string localizedValue)
|
||||
{
|
||||
if (!IsLocalizedParam(source))
|
||||
return false;
|
||||
|
||||
string key = GetL10NParamKey(source);
|
||||
string marker = string.IsNullOrEmpty(key)
|
||||
? "⟦empty⟧"
|
||||
: $"⟦{key}⟧";
|
||||
return string.Equals(localizedValue, marker, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保本地化系统已初始化
|
||||
/// </summary>
|
||||
|
||||
@@ -230,7 +230,7 @@ namespace AibisDream.MiniGame.Language
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 目标粒子:chineseChars 随机单字;非目标粒子:从笑话/干扰短句字符中随机。
|
||||
/// 目标粒子:从本轮本地化默认池随机;非目标粒子:从笑话/干扰短句字符中随机。
|
||||
/// 使用 IsTarget 判断,与红蓝颜色解绑。
|
||||
/// </summary>
|
||||
protected override string GetNextDisplayChar()
|
||||
@@ -264,7 +264,7 @@ namespace AibisDream.MiniGame.Language
|
||||
private void UpdateFocusInterferenceCharacters()
|
||||
{
|
||||
if (focusInterferenceProgress <= 0f ||
|
||||
string.IsNullOrEmpty(overrideCharPool) ||
|
||||
!HasOverrideCharacterPool ||
|
||||
(!isStatic && !isCalmed))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -54,12 +54,10 @@ namespace AibisDream.MiniGame.Language
|
||||
menuName = AibisAssetMenus.HuoShanExpressionContent)]
|
||||
public sealed class ExpressionContentCatalog : ScriptableObject
|
||||
{
|
||||
[SerializeField] private string actorLoopReference;
|
||||
[SerializeField] private List<ExpressionRoundDefinition> rounds = new();
|
||||
|
||||
private Dictionary<string, ExpressionRoundDefinition> roundById;
|
||||
|
||||
public string ActorLoopReference => actorLoopReference;
|
||||
public IReadOnlyList<ExpressionRoundDefinition> Rounds => rounds;
|
||||
|
||||
public bool TryGetRound(string roundId, out ExpressionRoundDefinition definition)
|
||||
@@ -77,9 +75,6 @@ namespace AibisDream.MiniGame.Language
|
||||
public List<string> GetValidationErrors()
|
||||
{
|
||||
var errors = new List<string>();
|
||||
if (!LocalizationKit.IsLocalizedParam(actorLoopReference))
|
||||
errors.Add("Actor Loop 必须配置为 l10n.* 引用。");
|
||||
|
||||
var ids = new HashSet<string>(StringComparer.Ordinal);
|
||||
if (rounds == null || rounds.Count == 0)
|
||||
{
|
||||
|
||||
@@ -20,15 +20,6 @@ namespace AibisDream
|
||||
[SerializeField] private ExpressionScreenPresentationController screenPresentation;
|
||||
[SerializeField] private ExpressionContentCatalog contentCatalog;
|
||||
|
||||
[Header("默认配置")]
|
||||
[SerializeField] private List<string> defaultAnxietyPhrases = new List<string>
|
||||
{
|
||||
"哈哈",
|
||||
"嘿嘿",
|
||||
"呵呵"
|
||||
};
|
||||
[SerializeField] private string defaultTargetSentence = "这是一个测试示例";
|
||||
|
||||
[Header("Sprite 淡入淡出")]
|
||||
[SerializeField] private SpriteRenderer screenOverlayRenderer;
|
||||
[SerializeField] private SpriteRenderer volcanoOverlayRenderer;
|
||||
@@ -162,20 +153,37 @@ namespace AibisDream
|
||||
/// </summary>
|
||||
/// <param name="anxietyPhrases">干扰短句列表(笑话等,非目标粒子从中随机取字符)</param>
|
||||
/// <param name="targetSentence">目标句子</param>
|
||||
/// <param name="defaultCharacterPool">当前 Locale 的默认随机字符池</param>
|
||||
/// <param name="completionNodeName">完成时触发的对话节点名称(可选)</param>
|
||||
/// <param name="nonTargetParticleTotal">非目标候选粒子总数;<0 时使用 Inspector 默认候选池大小</param>
|
||||
public void StartSystem(List<string> anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1)
|
||||
public void StartSystem(
|
||||
List<string> anxietyPhrases,
|
||||
string targetSentence,
|
||||
IReadOnlyList<string> defaultCharacterPool,
|
||||
string completionNodeName = null,
|
||||
int nonTargetParticleTotal = -1)
|
||||
{
|
||||
HideSpriteOverlaysForMinigame();
|
||||
|
||||
if (particleManager != null)
|
||||
{
|
||||
particleManager.SetPresentationController(logReleasePresentation);
|
||||
// 使用传入的配置,如果没有则使用默认配置
|
||||
var phrases = anxietyPhrases ?? defaultAnxietyPhrases;
|
||||
var sentence = targetSentence ?? defaultTargetSentence;
|
||||
|
||||
particleManager.InitializeSystem(phrases, sentence, completionNodeName, nonTargetParticleTotal);
|
||||
if (anxietyPhrases == null || anxietyPhrases.Count == 0 ||
|
||||
string.IsNullOrWhiteSpace(targetSentence) ||
|
||||
defaultCharacterPool == null ||
|
||||
defaultCharacterPool.Count == 0)
|
||||
{
|
||||
Debug.LogError(
|
||||
"[ExpressionManager] 启动参数缺少目标文字、干扰 token 或默认字符池;系统未启动。");
|
||||
return;
|
||||
}
|
||||
|
||||
particleManager.InitializeSystem(
|
||||
anxietyPhrases,
|
||||
targetSentence,
|
||||
defaultCharacterPool,
|
||||
completionNodeName,
|
||||
nonTargetParticleTotal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,10 +219,20 @@ namespace AibisDream
|
||||
/// <summary>
|
||||
/// 启动粒子系统(字符串数组版本,方便 Yarn 调用)
|
||||
/// </summary>
|
||||
public void StartSystem(string[] anxietyPhrases, string targetSentence, string completionNodeName = null, int nonTargetParticleTotal = -1)
|
||||
public void StartSystem(
|
||||
string[] anxietyPhrases,
|
||||
string targetSentence,
|
||||
IReadOnlyList<string> defaultCharacterPool,
|
||||
string completionNodeName = null,
|
||||
int nonTargetParticleTotal = -1)
|
||||
{
|
||||
List<string> phrasesList = anxietyPhrases != null ? new List<string>(anxietyPhrases) : null;
|
||||
StartSystem(phrasesList, targetSentence, completionNodeName, nonTargetParticleTotal);
|
||||
StartSystem(
|
||||
phrasesList,
|
||||
targetSentence,
|
||||
defaultCharacterPool,
|
||||
completionNodeName,
|
||||
nonTargetParticleTotal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -921,28 +939,6 @@ namespace AibisDream
|
||||
targetOpacity);
|
||||
}
|
||||
|
||||
public IEnumerator BeginExpressionLie(string keyword, string preset)
|
||||
{
|
||||
if (logReleasePresentation == null)
|
||||
{
|
||||
Debug.LogError("[ExpressionManager] LogReleasePresentationController 未配置;谎话命令安全结束。");
|
||||
yield break;
|
||||
}
|
||||
yield return logReleasePresentation.BeginLie(keyword, preset);
|
||||
}
|
||||
|
||||
public IEnumerator BreakExpressionLie()
|
||||
{
|
||||
if (logReleasePresentation == null) yield break;
|
||||
yield return logReleasePresentation.BreakLie();
|
||||
}
|
||||
|
||||
public IEnumerator RevealExpressionTruth()
|
||||
{
|
||||
if (logReleasePresentation == null) yield break;
|
||||
yield return logReleasePresentation.RevealTruth();
|
||||
}
|
||||
|
||||
public IEnumerator EndExpressionMemory()
|
||||
{
|
||||
if (logReleasePresentation == null) yield break;
|
||||
|
||||
@@ -4,8 +4,8 @@ using System.Globalization;
|
||||
namespace AibisDream.MiniGame.Language
|
||||
{
|
||||
/// <summary>
|
||||
/// 将玩家可见文字按 Unicode 文本元素拆分。空白保留在布局中,但不生成粒子。
|
||||
/// 随机粒子字符池不使用本类,维持现有行为。
|
||||
/// 将玩家可见文字按 Unicode 文本元素拆分。
|
||||
/// 空白保留在布局中但不生成粒子;随机字符池直接忽略空白。
|
||||
/// </summary>
|
||||
public static class ExpressionTextTokenizer
|
||||
{
|
||||
@@ -39,6 +39,17 @@ namespace AibisDream.MiniGame.Language
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<string> BuildCharacterPool(IEnumerable<string> texts)
|
||||
{
|
||||
var result = new List<string>();
|
||||
if (texts == null)
|
||||
return result;
|
||||
|
||||
foreach (string text in texts)
|
||||
result.AddRange(GetVisibleElements(text));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Layout BuildLayout(
|
||||
string text,
|
||||
float characterSpacing,
|
||||
|
||||
@@ -64,8 +64,10 @@ namespace AibisDream.MiniGame.Language
|
||||
[SerializeField] private float textMargin = 1f;
|
||||
|
||||
[Header("游戏配置")]
|
||||
[SerializeField] private List<string> anxietyPhrases = new List<string>(); // 干扰短句(笑话等,用于非目标粒子,Yarn 会覆盖)
|
||||
[SerializeField] private string targetSentence = "这是一个测试示例"; // 目标句子
|
||||
private List<string> anxietyPhrases = new List<string>();
|
||||
private string targetSentence = string.Empty;
|
||||
private IReadOnlyList<string> defaultCharacterPool = new List<string>().AsReadOnly();
|
||||
private IReadOnlyList<string> interferenceCharacterPool = new List<string>().AsReadOnly();
|
||||
[SerializeField] private string completionDialogNode = ""; // 完成时触发的对话节点
|
||||
|
||||
[Header("波形参数")]
|
||||
@@ -346,15 +348,39 @@ namespace AibisDream.MiniGame.Language
|
||||
/// </summary>
|
||||
/// <param name="phrases">焦虑短句列表</param>
|
||||
/// <param name="sentence">目标句子</param>
|
||||
/// <param name="localizedDefaultPool">当前 Locale 的默认随机字符池</param>
|
||||
/// <param name="dialogNode">完成时触发的对话节点</param>
|
||||
/// <param name="nonTargetParticleTotal">
|
||||
/// 非目标候选粒子总数(蓝/干扰侧可交互粒子数量)。≥0 时候选池大小 = 目标句字数 + 该值;<0 时使用 Inspector 的 Candidate Count。
|
||||
/// </param>
|
||||
public void InitializeSystem(List<string> phrases, string sentence, string dialogNode = null, int nonTargetParticleTotal = -1)
|
||||
public void InitializeSystem(
|
||||
List<string> phrases,
|
||||
string sentence,
|
||||
IReadOnlyList<string> localizedDefaultPool,
|
||||
string dialogNode = null,
|
||||
int nonTargetParticleTotal = -1)
|
||||
{
|
||||
CaptureDefaultCandidateCountFromInspector();
|
||||
|
||||
string useSentence = string.IsNullOrEmpty(sentence) ? "这是一个测试示例" : sentence;
|
||||
if (phrases == null || phrases.Count == 0 || string.IsNullOrWhiteSpace(sentence))
|
||||
{
|
||||
Debug.LogError(
|
||||
"[LanguageParticleManager] 目标文字或干扰 token 为空;初始化已中止。");
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> resolvedDefaultPool =
|
||||
ExpressionTextTokenizer.BuildCharacterPool(localizedDefaultPool);
|
||||
List<string> resolvedInterferencePool =
|
||||
ExpressionTextTokenizer.BuildCharacterPool(phrases);
|
||||
if (resolvedDefaultPool.Count == 0 || resolvedInterferencePool.Count == 0)
|
||||
{
|
||||
Debug.LogError(
|
||||
"[LanguageParticleManager] 默认字符池或干扰字符池没有有效的 Unicode 文本元素;初始化已中止。");
|
||||
return;
|
||||
}
|
||||
|
||||
string useSentence = sentence;
|
||||
int visibleTargetCount = ExpressionTextTokenizer.GetVisibleElements(useSentence).Count;
|
||||
if (visibleTargetCount == 0)
|
||||
{
|
||||
@@ -373,6 +399,13 @@ namespace AibisDream.MiniGame.Language
|
||||
candidateCount = defaultCandidateCountFromInspector;
|
||||
}
|
||||
|
||||
// 在创建或复用粒子前先提交本轮不可变快照,防止首帧或上一轮字符泄漏。
|
||||
anxietyPhrases = new List<string>(phrases);
|
||||
targetSentence = useSentence;
|
||||
completionDialogNode = dialogNode ?? "";
|
||||
defaultCharacterPool = resolvedDefaultPool.AsReadOnly();
|
||||
interferenceCharacterPool = resolvedInterferencePool.AsReadOnly();
|
||||
|
||||
// 如果是第一次初始化,先执行基础初始化
|
||||
if (!isInitialized)
|
||||
{
|
||||
@@ -407,16 +440,9 @@ namespace AibisDream.MiniGame.Language
|
||||
else
|
||||
{
|
||||
EnsureCandidatePoolSize(candidateCount);
|
||||
ApplyCharacterPoolsToParticles();
|
||||
}
|
||||
|
||||
// 设置游戏配置
|
||||
anxietyPhrases = phrases ?? new List<string>();
|
||||
targetSentence = useSentence;
|
||||
completionDialogNode = dialogNode ?? "";
|
||||
|
||||
// 设置全局焦虑短句配置
|
||||
TextParticle.SetAnxietyPhrases(anxietyPhrases);
|
||||
|
||||
// 根据目标句子更新目标粒子数量
|
||||
redParticleCount = Mathf.Min(visibleTargetCount, candidateCount);
|
||||
|
||||
@@ -700,6 +726,7 @@ namespace AibisDream.MiniGame.Language
|
||||
Vector3 pos = GetRandomPositionInBounds();
|
||||
GameObject obj = CreateParticleObject(pos, $"FloatingParticle_{i}");
|
||||
FloatingTextParticle particle = obj.AddComponent<FloatingTextParticle>();
|
||||
particle.SetCharacterPools(defaultCharacterPool, interferenceCharacterPool);
|
||||
particle.SetFont(chineseFontAsset, chineseFontMaterial);
|
||||
particle.SetColor(floatingTextColor);
|
||||
particle.SetFontSize(floatingTextFontSize);
|
||||
@@ -744,6 +771,7 @@ namespace AibisDream.MiniGame.Language
|
||||
Vector3 pos = GetRandomPositionInBounds();
|
||||
GameObject obj = CreateParticleObject(pos, $"CandidateParticle_{index}");
|
||||
CandidateParticle particle = obj.AddComponent<CandidateParticle>();
|
||||
particle.SetCharacterPools(defaultCharacterPool, interferenceCharacterPool);
|
||||
particle.SetFont(chineseFontAsset, chineseFontMaterial);
|
||||
particle.SetColors(targetParticleColor, nonTargetParticleColor, nonTargetParticleColor, targetCalmColor);
|
||||
particle.SetNonTargetMarquee(
|
||||
@@ -760,6 +788,22 @@ namespace AibisDream.MiniGame.Language
|
||||
candidateParticles.Add(particle);
|
||||
}
|
||||
|
||||
private void ApplyCharacterPoolsToParticles()
|
||||
{
|
||||
foreach (CandidateParticle particle in candidateParticles)
|
||||
particle?.SetCharacterPools(defaultCharacterPool, interferenceCharacterPool);
|
||||
foreach (FloatingTextParticle particle in floatingParticles)
|
||||
particle?.SetCharacterPools(defaultCharacterPool, interferenceCharacterPool);
|
||||
}
|
||||
|
||||
private void RefreshAllRandomCharacters()
|
||||
{
|
||||
foreach (CandidateParticle particle in candidateParticles)
|
||||
particle?.InitializeRandomCharacter();
|
||||
foreach (FloatingTextParticle particle in floatingParticles)
|
||||
particle?.InitializeRandomCharacter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 增删候选粒子以匹配目标数量(用于 Yarn 动态指定非目标粒子总数后复用同一视图)
|
||||
/// </summary>
|
||||
@@ -2016,6 +2060,8 @@ namespace AibisDream.MiniGame.Language
|
||||
Debug.LogWarning("[LanguageParticleManager] 老虎机换字字符池为空;已安全跳过。");
|
||||
return;
|
||||
}
|
||||
IReadOnlyList<string> poolElements =
|
||||
ExpressionTextTokenizer.GetVisibleElements(pool).AsReadOnly();
|
||||
|
||||
float changeInterval = Mathf.Max(0.02f, interval);
|
||||
foreach (TargetFocusVisual visual in focusVisuals.Values)
|
||||
@@ -2023,7 +2069,7 @@ namespace AibisDream.MiniGame.Language
|
||||
if (visual?.particle == null)
|
||||
continue;
|
||||
|
||||
visual.particle.SetOverrideCharPool(pool);
|
||||
visual.particle.SetOverrideCharacterPool(poolElements);
|
||||
visual.particle.SetChangeInterval(changeInterval);
|
||||
visual.particle.ResetChangeTimer(changeInterval);
|
||||
visual.particle.isStatic = false;
|
||||
@@ -2040,8 +2086,12 @@ namespace AibisDream.MiniGame.Language
|
||||
{
|
||||
if (string.IsNullOrEmpty(charPool))
|
||||
return null;
|
||||
string cleaned = charPool.Replace(" ", string.Empty).Replace("|", string.Empty);
|
||||
return cleaned.Length == 0 ? null : cleaned;
|
||||
string withoutSeparators = charPool
|
||||
.Replace("|", string.Empty)
|
||||
.Replace("|", string.Empty);
|
||||
List<string> elements =
|
||||
ExpressionTextTokenizer.GetVisibleElements(withoutSeparators);
|
||||
return elements.Count == 0 ? null : string.Concat(elements);
|
||||
}
|
||||
|
||||
public void StopSlotMachineShuffle()
|
||||
@@ -2053,7 +2103,7 @@ namespace AibisDream.MiniGame.Language
|
||||
}
|
||||
|
||||
foreach (CandidateParticle particle in targetParticles)
|
||||
particle?.SetOverrideCharPool(null);
|
||||
particle?.SetOverrideCharacterPool(null);
|
||||
}
|
||||
|
||||
private IEnumerator EndSlotMachineShuffleAfter(float duration)
|
||||
@@ -2061,7 +2111,7 @@ namespace AibisDream.MiniGame.Language
|
||||
yield return new WaitForSeconds(duration);
|
||||
slotMachineRoutine = null;
|
||||
foreach (CandidateParticle particle in targetParticles)
|
||||
particle?.SetOverrideCharPool(null);
|
||||
particle?.SetOverrideCharacterPool(null);
|
||||
}
|
||||
|
||||
public void PrepareTruthReleaseFromLie()
|
||||
@@ -3151,6 +3201,7 @@ namespace AibisDream.MiniGame.Language
|
||||
RebuildScreenClipMaterials();
|
||||
|
||||
SelectRedParticles();
|
||||
RefreshAllRandomCharacters();
|
||||
UpdateStatusUI();
|
||||
if (playEntranceEffect)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
||||
using AibisDream.FixSystem;
|
||||
using AibisDream;
|
||||
using AibisDream.Framework;
|
||||
using AibisDream.Utility;
|
||||
using UnityEngine.Localization;
|
||||
using UnityEngine.Localization.Settings;
|
||||
using Yarn.Unity;
|
||||
@@ -38,9 +39,19 @@ namespace AibisDream.MiniGame.Language
|
||||
while (!all.IsCompleted)
|
||||
yield return null;
|
||||
|
||||
completed?.Invoke(all.Status == TaskStatus.RanToCompletion
|
||||
? all.Result
|
||||
: System.Array.Empty<string>());
|
||||
if (all.Status != TaskStatus.RanToCompletion)
|
||||
{
|
||||
completed?.Invoke(System.Array.Empty<string>());
|
||||
yield break;
|
||||
}
|
||||
|
||||
string[] values = all.Result;
|
||||
for (int i = 0; i < values.Length; i++)
|
||||
{
|
||||
if (LocalizationKit.IsMissingParamResult(references[i], values[i]))
|
||||
values[i] = null;
|
||||
}
|
||||
completed?.Invoke(values);
|
||||
}
|
||||
|
||||
private static bool ValidateRequiredText(string value, string command, string parameter)
|
||||
@@ -77,6 +88,22 @@ namespace AibisDream.MiniGame.Language
|
||||
return tokens.Count > 0;
|
||||
}
|
||||
|
||||
public static bool TryParseExpressionPool(
|
||||
string value,
|
||||
out List<string> characters)
|
||||
{
|
||||
characters = new List<string>();
|
||||
if (string.IsNullOrWhiteSpace(value) ||
|
||||
value.Contains('|') ||
|
||||
value.Contains('|'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
characters = ExpressionTextTokenizer.GetVisibleElements(value);
|
||||
return characters.Count > 0;
|
||||
}
|
||||
|
||||
private static bool TryGetPresentation(out LogReleasePresentationController presentation)
|
||||
{
|
||||
presentation = ExpressionManager != null
|
||||
@@ -130,10 +157,12 @@ namespace AibisDream.MiniGame.Language
|
||||
locale,
|
||||
values => localized = values,
|
||||
anxietyPhrasesStr,
|
||||
targetSentence);
|
||||
if (localized == null || localized.Length != 2 ||
|
||||
targetSentence,
|
||||
ConstRef.ExpressParticlePoolParam);
|
||||
if (localized == null || localized.Length != 3 ||
|
||||
!ValidateRequiredText(localized[0], "start_expression", nameof(anxietyPhrasesStr)) ||
|
||||
!ValidateRequiredText(localized[1], "start_expression", nameof(targetSentence)))
|
||||
!ValidateRequiredText(localized[1], "start_expression", nameof(targetSentence)) ||
|
||||
!ValidateRequiredText(localized[2], "start_expression", "defaultCharacterPool"))
|
||||
yield break;
|
||||
|
||||
if (!TryParseExpressionTokens(localized[0], out List<string> phrases))
|
||||
@@ -142,6 +171,12 @@ namespace AibisDream.MiniGame.Language
|
||||
"[LanguageYarnCommand] start_expression 没有有效的干扰 token;命令已安全结束。");
|
||||
yield break;
|
||||
}
|
||||
if (!TryParseExpressionPool(localized[2], out List<string> defaultCharacterPool))
|
||||
{
|
||||
UnityEngine.Debug.LogError(
|
||||
"[LanguageYarnCommand] start_expression 的默认字符池无效;命令已安全结束。");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// 先清理上一轮演出,再淡出旧文字/连线并恢复 Log 操作界面。
|
||||
yield return ExpressionManager.PreparePresentationForNextRound();
|
||||
@@ -151,6 +186,7 @@ namespace AibisDream.MiniGame.Language
|
||||
ExpressionManager.StartSystem(
|
||||
phrases,
|
||||
localized[1],
|
||||
defaultCharacterPool,
|
||||
completionNode,
|
||||
nonTargetParticleTotal);
|
||||
yield return ExpressionManager.WaitUntilExpressionFlowReady();
|
||||
@@ -199,10 +235,12 @@ namespace AibisDream.MiniGame.Language
|
||||
locale,
|
||||
values => localized = values,
|
||||
round.TokenReference,
|
||||
round.TargetReference);
|
||||
if (localized == null || localized.Length != 2 ||
|
||||
round.TargetReference,
|
||||
ConstRef.ExpressParticlePoolParam);
|
||||
if (localized == null || localized.Length != 3 ||
|
||||
!ValidateRequiredText(localized[0], "start_expression_round", nameof(round.TokenReference)) ||
|
||||
!ValidateRequiredText(localized[1], "start_expression_round", nameof(round.TargetReference)))
|
||||
!ValidateRequiredText(localized[1], "start_expression_round", nameof(round.TargetReference)) ||
|
||||
!ValidateRequiredText(localized[2], "start_expression_round", "defaultCharacterPool"))
|
||||
yield break;
|
||||
|
||||
if (!TryParseExpressionTokens(localized[0], out List<string> phrases))
|
||||
@@ -211,6 +249,12 @@ namespace AibisDream.MiniGame.Language
|
||||
$"[LanguageYarnCommand] 表达轮次 '{roundId}' 没有有效的干扰 token。");
|
||||
yield break;
|
||||
}
|
||||
if (!TryParseExpressionPool(localized[2], out List<string> defaultCharacterPool))
|
||||
{
|
||||
UnityEngine.Debug.LogError(
|
||||
$"[LanguageYarnCommand] 表达轮次 '{roundId}' 的默认字符池无效。");
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return ExpressionManager.PreparePresentationForNextRound();
|
||||
yield return ExpressionManager.FadeOutParticlesBeforeNewRound(0f);
|
||||
@@ -219,6 +263,7 @@ namespace AibisDream.MiniGame.Language
|
||||
ExpressionManager.StartSystem(
|
||||
phrases,
|
||||
localized[1],
|
||||
defaultCharacterPool,
|
||||
completionNode ?? string.Empty,
|
||||
round.NonTargetParticleCount);
|
||||
yield return ExpressionManager.WaitUntilExpressionFlowReady();
|
||||
@@ -379,49 +424,6 @@ namespace AibisDream.MiniGame.Language
|
||||
presentation.BeginFaceEntry(duration));
|
||||
}
|
||||
|
||||
[YarnCommand("expression_lie_begin")]
|
||||
public static IEnumerator ExpressionLieBegin(string keyword, string preset)
|
||||
{
|
||||
if (ExpressionManager == null)
|
||||
{
|
||||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||||
yield break;
|
||||
}
|
||||
string[] localized = null;
|
||||
yield return ResolveTextReferences(
|
||||
GetCommandLocale(),
|
||||
values => localized = values,
|
||||
keyword);
|
||||
if (localized == null || localized.Length != 1 ||
|
||||
!ValidateRequiredText(localized[0], "expression_lie_begin", nameof(keyword)))
|
||||
yield break;
|
||||
|
||||
yield return ExpressionManager.StartCoroutine(
|
||||
ExpressionManager.BeginExpressionLie(localized[0], preset));
|
||||
}
|
||||
|
||||
[YarnCommand("expression_lie_break")]
|
||||
public static IEnumerator ExpressionLieBreak()
|
||||
{
|
||||
if (ExpressionManager == null)
|
||||
{
|
||||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||||
yield break;
|
||||
}
|
||||
yield return ExpressionManager.StartCoroutine(ExpressionManager.BreakExpressionLie());
|
||||
}
|
||||
|
||||
[YarnCommand("expression_truth_reveal")]
|
||||
public static IEnumerator ExpressionTruthReveal()
|
||||
{
|
||||
if (ExpressionManager == null)
|
||||
{
|
||||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||||
yield break;
|
||||
}
|
||||
yield return ExpressionManager.StartCoroutine(ExpressionManager.RevealExpressionTruth());
|
||||
}
|
||||
|
||||
// 以下命令是一动作一命令。非 IEnumerator 命令只启动效果,节拍由 Yarn 的 wait 控制。
|
||||
|
||||
[YarnCommand("expression_lie_prepare")]
|
||||
@@ -540,7 +542,7 @@ namespace AibisDream.MiniGame.Language
|
||||
/// <summary>
|
||||
/// 结尾真假快速闪切(非阻塞,节奏由同长度 wait 控制):真心话与谎话交替独占屏幕,
|
||||
/// 两者的屏幕底色(电视头背景)不同,越到后面切得越快,结束定格在真话帧。
|
||||
/// <<expression_truth_lie_flicker "l10n.hs.exp.actor.loop" "l10n.hs.exp.log1.target" 3.2 0.24 12>>
|
||||
/// <<expression_truth_lie_flicker "l10n.hs.exp.log1.tokens" "l10n.hs.exp.log1.target" 3.2 0.24 12>>
|
||||
/// </summary>
|
||||
[YarnCommand("expression_truth_lie_flicker")]
|
||||
public static IEnumerator ExpressionTruthLieFlicker(
|
||||
@@ -719,54 +721,6 @@ namespace AibisDream.MiniGame.Language
|
||||
true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 非阻塞启动“没问题 → 冇问题 → 帽问题”三拍无限闪现循环。
|
||||
/// <<expression_actor_flash_loop_start>>
|
||||
/// </summary>
|
||||
[YarnCommand("expression_actor_flash_loop_start")]
|
||||
public static IEnumerator ExpressionActorFlashLoopStart()
|
||||
{
|
||||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||||
yield break;
|
||||
|
||||
ExpressionContentCatalog catalog = ExpressionManager.ContentCatalog;
|
||||
if (catalog == null || !LocalizationKit.IsLocalizedParam(catalog.ActorLoopReference))
|
||||
{
|
||||
UnityEngine.Debug.LogError(
|
||||
"[LanguageYarnCommand] Actor Loop 本地化引用未配置;命令已安全结束。");
|
||||
yield break;
|
||||
}
|
||||
|
||||
string[] localized = null;
|
||||
yield return ResolveTextReferences(
|
||||
GetCommandLocale(),
|
||||
values => localized = values,
|
||||
catalog.ActorLoopReference);
|
||||
if (localized == null || localized.Length != 1 ||
|
||||
!ValidateRequiredText(localized[0], "expression_actor_flash_loop_start", "ActorLoopReference"))
|
||||
yield break;
|
||||
|
||||
if (!TryParseExpressionTokens(localized[0], out List<string> phrases))
|
||||
{
|
||||
UnityEngine.Debug.LogError(
|
||||
"[LanguageYarnCommand] Actor Loop 短句列表无效;命令已安全结束。");
|
||||
yield break;
|
||||
}
|
||||
|
||||
presentation.StartActorFlashLoop(phrases);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 立即停止无限闪现循环、清除当前大字,并恢复晃动粒子字。
|
||||
/// <<expression_actor_flash_loop_stop>>
|
||||
/// </summary>
|
||||
[YarnCommand("expression_actor_flash_loop_stop")]
|
||||
public static void ExpressionActorFlashLoopStop()
|
||||
{
|
||||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||||
presentation.StopActorFlashLoop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 真话闪现(阻塞):谎话大字之间,暖色完整真话带细微抖动挣扎浮现一拍;
|
||||
/// "被掐断"由紧随其后的 expression_glitch_pulse 表现。不推进谎话冲击等级。
|
||||
@@ -799,7 +753,7 @@ namespace AibisDream.MiniGame.Language
|
||||
/// <summary>
|
||||
/// 老虎机换字(非阻塞,节拍由 Yarn wait 控制):聚焦字在 duration 内只从 charPool 里疯狂换字。
|
||||
/// truthPool 非空时其字符也混入换字池,并以真话暖色显示。
|
||||
/// <<expression_actor_slot "l10n.hs.exp.actor.loop" 5 0.05 14 "l10n.hs.exp.log1.target">>
|
||||
/// <<expression_actor_slot "l10n.hs.exp.log1.tokens" 5 0.05 14 "l10n.hs.exp.log1.target">>
|
||||
/// </summary>
|
||||
[YarnCommand("expression_actor_slot")]
|
||||
public static IEnumerator ExpressionActorSlot(
|
||||
@@ -882,7 +836,7 @@ namespace AibisDream.MiniGame.Language
|
||||
|
||||
/// <summary>
|
||||
/// 单列词组严格裁在表情屏内,像字幕一样无缝向下滚动。
|
||||
/// <<expression_actor_scroll "l10n.hs.exp.actor.loop" 1.8 0.85 0.45 5.2>>
|
||||
/// <<expression_actor_scroll "l10n.hs.exp.log1.tokens" 1.8 0.85 0.45 5.2>>
|
||||
/// </summary>
|
||||
[YarnCommand("expression_actor_scroll")]
|
||||
public static IEnumerator ExpressionActorScroll(
|
||||
|
||||
@@ -38,34 +38,19 @@ namespace AibisDream.MiniGame.Language
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly float BaseDistortion;
|
||||
public readonly float LiePause;
|
||||
public readonly float BreakPeak;
|
||||
public readonly float BreakDuration;
|
||||
public readonly float TruthDuration;
|
||||
public readonly float TruthPeripheral;
|
||||
|
||||
public Preset(
|
||||
string name,
|
||||
float baseDistortion,
|
||||
float liePause,
|
||||
float breakPeak,
|
||||
float breakDuration,
|
||||
float truthDuration,
|
||||
float truthPeripheral)
|
||||
float baseDistortion)
|
||||
{
|
||||
Name = name;
|
||||
BaseDistortion = baseDistortion;
|
||||
LiePause = liePause;
|
||||
BreakPeak = breakPeak;
|
||||
BreakDuration = breakDuration;
|
||||
TruthDuration = truthDuration;
|
||||
TruthPeripheral = truthPeripheral;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Preset LightPreset = new Preset("Light", 0.10f, 0.35f, 0.25f, 0.25f, 0.70f, 0.15f);
|
||||
private static readonly Preset MediumPreset = new Preset("Medium", 0.20f, 0.55f, 0.50f, 0.35f, 0.90f, 0.30f);
|
||||
private static readonly Preset HeavyPreset = new Preset("Heavy", 0.35f, 0.80f, 0.85f, 0.50f, 1.10f, 0.50f);
|
||||
private static readonly Preset LightPreset = new Preset("Light", 0.10f);
|
||||
private static readonly Preset MediumPreset = new Preset("Medium", 0.20f);
|
||||
private static readonly Preset HeavyPreset = new Preset("Heavy", 0.35f);
|
||||
|
||||
[Header("Scene References")]
|
||||
[SerializeField] private LanguageParticleManager particleManager;
|
||||
@@ -96,7 +81,6 @@ namespace AibisDream.MiniGame.Language
|
||||
|
||||
[Header("Screen Styling")]
|
||||
[SerializeField] private Color lieColor = new Color(0.66f, 0.94f, 1f, 1f);
|
||||
[SerializeField] private Color errorColor = new Color(1f, 0.34f, 0.18f, 1f);
|
||||
[SerializeField] private Color truthWarmColor = new Color(1f, 1f, 1f, 1f);
|
||||
[Tooltip("爆发段真话闪现与老虎机真话字符的暖色;需与谎话的冷白明显区分。")]
|
||||
[SerializeField] private Color truthAccentColor = new Color(1f, 0.84f, 0.6f, 1f);
|
||||
@@ -118,7 +102,6 @@ namespace AibisDream.MiniGame.Language
|
||||
private Preset currentPreset = LightPreset;
|
||||
private MemoryKind currentMemoryKind = MemoryKind.Office;
|
||||
private string currentLieKeyword = string.Empty;
|
||||
private string currentLeakFragment = string.Empty;
|
||||
|
||||
private Transform runtimeRoot;
|
||||
private SpriteRenderer memoryRendererA;
|
||||
@@ -129,7 +112,6 @@ namespace AibisDream.MiniGame.Language
|
||||
private SpriteMask screenSpriteMask;
|
||||
private TextMeshPro lieText;
|
||||
private TextMeshPro lieGhostText;
|
||||
private TextMeshPro systemText;
|
||||
private TextMeshPro actorFlashText;
|
||||
private TextMeshPro actorFlashChromaRed;
|
||||
private TextMeshPro actorFlashChromaCyan;
|
||||
@@ -141,7 +123,6 @@ namespace AibisDream.MiniGame.Language
|
||||
private string truthWarmColorHex;
|
||||
private TMPRectClipper lieClipper;
|
||||
private TMPRectClipper lieGhostClipper;
|
||||
private TMPRectClipper systemClipper;
|
||||
private TMPRectClipper actorFlashClipper;
|
||||
private TMPRectClipper actorFlashChromaRedClipper;
|
||||
private TMPRectClipper actorFlashChromaCyanClipper;
|
||||
@@ -150,7 +131,6 @@ namespace AibisDream.MiniGame.Language
|
||||
private bool actorFlashActive;
|
||||
private int actorFlashCount;
|
||||
private Coroutine actorOverdriveRoutine;
|
||||
private Coroutine actorFlashLoopRoutine;
|
||||
private Texture2D runtimeWhiteTexture;
|
||||
private Sprite runtimeWhiteSprite;
|
||||
private MaterialPropertyBlock memoryBlockA;
|
||||
@@ -254,7 +234,6 @@ namespace AibisDream.MiniGame.Language
|
||||
TweenMemoryOpacity(memoryRendererB, 0f, duration);
|
||||
FadeText(lieText, 0f, duration);
|
||||
FadeText(lieGhostText, 0f, duration);
|
||||
FadeText(systemText, 0f, duration);
|
||||
FadeText(actorFlashText, 0f, duration);
|
||||
FadeText(actorFlashChromaRed, 0f, duration);
|
||||
FadeText(actorFlashChromaCyan, 0f, duration);
|
||||
@@ -337,7 +316,6 @@ namespace AibisDream.MiniGame.Language
|
||||
|
||||
currentPreset = preset;
|
||||
currentMemoryKind = kind;
|
||||
currentLeakFragment = GetLeakFragment(kind);
|
||||
_ = horizontalSpeed;
|
||||
actorFlashCount = 0;
|
||||
memoryImpact = 0f;
|
||||
@@ -398,84 +376,6 @@ namespace AibisDream.MiniGame.Language
|
||||
Mathf.Max(0.01f, duration));
|
||||
}
|
||||
|
||||
public IEnumerator BeginLie(string keyword, string presetName)
|
||||
{
|
||||
EnsureRuntimeObjects();
|
||||
if (!TryResolvePreset(presetName, out Preset requestedPreset))
|
||||
{
|
||||
Debug.LogError($"[LogReleasePresentation] 未知演出预设“{presetName}”;命令安全结束。");
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (!string.Equals(requestedPreset.Name, currentPreset.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"[LogReleasePresentation] expression_lie_begin 的预设 {requestedPreset.Name} " +
|
||||
$"与当前记忆 {currentPreset.Name} 不一致,将沿用当前记忆预设。");
|
||||
}
|
||||
|
||||
PrepareLieVisuals(0.25f, 0.005f);
|
||||
SqueezeTruthCharacters(
|
||||
0.22f,
|
||||
0.055f + currentPreset.BreakPeak * 0.065f,
|
||||
0.28f);
|
||||
ShowLieKeyword(keyword, 0.22f, 14f);
|
||||
PlayScreenScanline(0.32f, currentMemoryKind == MemoryKind.Sunset);
|
||||
if (currentMemoryKind == MemoryKind.PrivateOffice)
|
||||
ShowSystemMessage("输出正常", "normal", 0.12f);
|
||||
else
|
||||
HideSystemMessageImmediate();
|
||||
yield return new WaitForSeconds(0.25f);
|
||||
}
|
||||
|
||||
public IEnumerator BreakLie()
|
||||
{
|
||||
EnsureRuntimeObjects();
|
||||
if (!ExpectState(nameof(BreakLie), PresentationState.LieHolding))
|
||||
yield break;
|
||||
|
||||
yield return new WaitForSeconds(currentPreset.LiePause);
|
||||
BeginLieCracking(0.48f);
|
||||
PlayScreenScanline(0.22f, true);
|
||||
PlayScreenDim(0.28f, 0.06f, 0.12f);
|
||||
PlayMemoryJolt(0.08f, 0.24f, 0.11f);
|
||||
yield return LeakTruthFragment(currentLeakFragment, 0.24f);
|
||||
|
||||
if (currentMemoryKind == MemoryKind.PrivateOffice)
|
||||
{
|
||||
HideSystemMessage(0.01f);
|
||||
yield return new WaitForSeconds(0.08f);
|
||||
ShowSystemMessage("输出与原始 log 不一致", "error", 0.08f);
|
||||
yield return PulseGlitch(HeavyPreset.BreakPeak, 0.12f, 0.18f);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator RevealTruth()
|
||||
{
|
||||
EnsureRuntimeObjects();
|
||||
if (!ExpectState(
|
||||
nameof(RevealTruth),
|
||||
PresentationState.LieCracking,
|
||||
PresentationState.LieHolding))
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
DOTween.Kill(this);
|
||||
yield return HoldTruthBlackout(0.055f, 0.42f);
|
||||
SplitLieVisual(currentPreset.BreakDuration, 0.18f, 1.85f, 0.42f);
|
||||
PlayTruthFlash(0.62f, 0.045f, 0.025f, 0.16f);
|
||||
PlayMemoryBrightnessPulse(0.16f, 0.06f, 0.20f);
|
||||
PlayMemoryTear(currentPreset.BreakPeak, currentPreset.BreakDuration);
|
||||
PlayGlitchPulse(currentPreset.BreakPeak, 0.12f, currentPreset.BreakDuration);
|
||||
PlayFaceDip(currentPreset.BreakDuration, faceDipRatio);
|
||||
ReleaseTruthCharacters(0.08f);
|
||||
yield return ResolveTruthCharacters(
|
||||
currentPreset.TruthDuration,
|
||||
currentPreset.TruthPeripheral,
|
||||
0.42f);
|
||||
}
|
||||
|
||||
public void PrepareLieVisuals(float freezeDuration, float noiseTarget)
|
||||
{
|
||||
EnsureRuntimeObjects();
|
||||
@@ -560,42 +460,6 @@ namespace AibisDream.MiniGame.Language
|
||||
PlayScanline(Mathf.Max(0.01f, duration), stalled);
|
||||
}
|
||||
|
||||
public void ShowSystemMessage(string value, string style, float fadeDuration)
|
||||
{
|
||||
EnsureRuntimeObjects();
|
||||
if (!ExpectState(
|
||||
nameof(ShowSystemMessage),
|
||||
PresentationState.FaceEntering,
|
||||
PresentationState.Memory,
|
||||
PresentationState.LieHolding,
|
||||
PresentationState.LieCracking))
|
||||
return;
|
||||
Color color;
|
||||
if (string.Equals(style, "error", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
color = errorColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.Equals(style, "normal", StringComparison.OrdinalIgnoreCase))
|
||||
Debug.LogWarning($"[LogReleasePresentation] 未知系统提示样式“{style}”,改用 normal。");
|
||||
color = new Color(0.58f, 0.78f, 0.82f, 1f);
|
||||
}
|
||||
SetSystemMessage(value ?? string.Empty, color, Mathf.Max(0.01f, fadeDuration));
|
||||
}
|
||||
|
||||
public void HideSystemMessage(float fadeDuration)
|
||||
{
|
||||
if (systemText == null || !systemText.gameObject.activeSelf)
|
||||
return;
|
||||
|
||||
systemText.DOKill();
|
||||
systemText.DOFade(0f, Mathf.Max(0.01f, fadeDuration))
|
||||
.SetEase(Ease.OutQuad)
|
||||
.SetTarget(this)
|
||||
.OnComplete(HideSystemMessageImmediate);
|
||||
}
|
||||
|
||||
public void BeginLieCracking(float lieAlpha)
|
||||
{
|
||||
if (!ExpectState(
|
||||
@@ -654,9 +518,9 @@ namespace AibisDream.MiniGame.Language
|
||||
PresentationState.LieCracking))
|
||||
yield break;
|
||||
|
||||
currentLeakFragment = fragment ?? string.Empty;
|
||||
string resolvedFragment = fragment ?? string.Empty;
|
||||
yield return ShuffleLieCharactersRoutine(
|
||||
currentLeakFragment,
|
||||
resolvedFragment,
|
||||
Mathf.Max(0.01f, duration),
|
||||
0.045f);
|
||||
}
|
||||
@@ -688,7 +552,6 @@ namespace AibisDream.MiniGame.Language
|
||||
return;
|
||||
|
||||
DOTween.Kill(this);
|
||||
StopActorFlashLoopImmediate(false);
|
||||
StopActorOverdriveImmediate();
|
||||
memoryPushSpeed = 0f;
|
||||
memoryPushProgress = 0f;
|
||||
@@ -808,73 +671,6 @@ namespace AibisDream.MiniGame.Language
|
||||
}
|
||||
}
|
||||
|
||||
public void StartActorFlashLoop(IReadOnlyList<string> phrases)
|
||||
{
|
||||
EnsureRuntimeObjects();
|
||||
if (!ExpectState(nameof(StartActorFlashLoop), PresentationState.Memory))
|
||||
return;
|
||||
if (phrases == null || phrases.Count == 0)
|
||||
{
|
||||
Debug.LogError("[LogReleasePresentation] Actor Flash Loop 没有有效短句;已安全跳过。");
|
||||
return;
|
||||
}
|
||||
|
||||
StopActorFlashLoopImmediate(true);
|
||||
actorFlashLoopRoutine = StartCoroutine(ActorFlashLoopRoutine(phrases));
|
||||
}
|
||||
|
||||
public void StopActorFlashLoop()
|
||||
{
|
||||
StopActorFlashLoopImmediate(true);
|
||||
}
|
||||
|
||||
private IEnumerator ActorFlashLoopRoutine(IReadOnlyList<string> phrases)
|
||||
{
|
||||
while (state == PresentationState.Memory)
|
||||
{
|
||||
for (int i = 0; i < phrases.Count && state == PresentationState.Memory; i++)
|
||||
{
|
||||
int style = i % 3;
|
||||
float hold = style == 2 ? 0.15f : 0.25f;
|
||||
float fontSize = style == 0 ? 10.5f : style == 1 ? 11.2f : 12f;
|
||||
float punchScale = style == 0 ? 1.45f : style == 1 ? 1.85f : 2.20f;
|
||||
float chromaDistance = style == 2 ? 0.010f : 0.008f;
|
||||
float chromaAlpha = style == 2 ? 0.08f : 0.06f;
|
||||
float impact = style == 0 ? 0.50f : style == 1 ? 0.72f : 1f;
|
||||
|
||||
yield return FlashActorLie(
|
||||
phrases[i],
|
||||
hold,
|
||||
fontSize,
|
||||
1.1f,
|
||||
punchScale,
|
||||
chromaDistance,
|
||||
chromaAlpha,
|
||||
impact,
|
||||
0f);
|
||||
if (state == PresentationState.Memory)
|
||||
yield return new WaitForSeconds(0.3f);
|
||||
}
|
||||
}
|
||||
|
||||
actorFlashLoopRoutine = null;
|
||||
}
|
||||
|
||||
private void StopActorFlashLoopImmediate(bool restoreParticles)
|
||||
{
|
||||
if (actorFlashLoopRoutine != null)
|
||||
{
|
||||
StopCoroutine(actorFlashLoopRoutine);
|
||||
actorFlashLoopRoutine = null;
|
||||
}
|
||||
|
||||
actorFlashActive = false;
|
||||
memoryImpact = 0f;
|
||||
HideActorFlashVisualsImmediate();
|
||||
if (restoreParticles && state == PresentationState.Memory)
|
||||
particleManager?.SetTargetParticlesAlpha(1f, 0f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 真话闪现:谎话大字之间,暖色完整真话带着细微抖动挣扎浮现一拍。
|
||||
/// 不推进谎话冲击等级、不累积记忆损伤;"被掐断"由紧随其后的 expression_glitch_pulse 表现。
|
||||
@@ -1100,7 +896,6 @@ namespace AibisDream.MiniGame.Language
|
||||
state = PresentationState.TruthResolving;
|
||||
StopLieShuffleImmediate(true);
|
||||
HideLieLayersImmediate();
|
||||
HideSystemMessageImmediate();
|
||||
StopScanline();
|
||||
StopScreenDim();
|
||||
|
||||
@@ -1706,7 +1501,6 @@ namespace AibisDream.MiniGame.Language
|
||||
return;
|
||||
|
||||
state = PresentationState.TruthResolving;
|
||||
HideSystemMessage(0.15f);
|
||||
particleManager?.PrepareTruthReleaseFromLie();
|
||||
particleManager?.ApplyPresentationResolvedVisuals(presentationResolvedColor);
|
||||
particleManager?.SetTargetParticlesAlpha(1f, Mathf.Max(0.01f, alphaDuration));
|
||||
@@ -1752,7 +1546,6 @@ namespace AibisDream.MiniGame.Language
|
||||
}
|
||||
|
||||
HideLieLayersImmediate();
|
||||
HideSystemMessageImmediate();
|
||||
StopScanline();
|
||||
StopScreenDim();
|
||||
ReleaseScreenTextClipMaterials();
|
||||
@@ -1790,7 +1583,6 @@ namespace AibisDream.MiniGame.Language
|
||||
TweenMemoryOpacity(memoryRendererA, 0f, memoryExitDuration);
|
||||
TweenMemoryOpacity(memoryRendererB, 0f, memoryExitDuration);
|
||||
HideLieLayersImmediate();
|
||||
HideSystemMessageImmediate();
|
||||
StopScanline();
|
||||
StopScreenDim();
|
||||
SilenceNoiseOverlayIfPresent();
|
||||
@@ -1815,7 +1607,6 @@ namespace AibisDream.MiniGame.Language
|
||||
{
|
||||
DOTween.Kill(this);
|
||||
StopAllCoroutines();
|
||||
actorFlashLoopRoutine = null;
|
||||
RestoreFaceSpriteImmediate();
|
||||
|
||||
memoryOpacityA = 0f;
|
||||
@@ -1824,7 +1615,6 @@ namespace AibisDream.MiniGame.Language
|
||||
DisableMemoryRenderer(memoryRendererB);
|
||||
activeMemoryRenderer = null;
|
||||
HideLieLayersImmediate();
|
||||
HideSystemMessageImmediate();
|
||||
StopScanline();
|
||||
StopScreenDim();
|
||||
ReleaseScreenTextClipMaterials();
|
||||
@@ -1847,7 +1637,6 @@ namespace AibisDream.MiniGame.Language
|
||||
particleManager?.RestoreTargetPresentationDefaults();
|
||||
state = PresentationState.Idle;
|
||||
currentLieKeyword = string.Empty;
|
||||
currentLeakFragment = string.Empty;
|
||||
memoryPushSpeed = 0f;
|
||||
memoryPushProgress = 0f;
|
||||
memoryZoom = 1f;
|
||||
@@ -2309,21 +2098,18 @@ namespace AibisDream.MiniGame.Language
|
||||
CreateScreenMask();
|
||||
lieText = CreateScreenText("LieKeyword", 4.1f, screenTextSortingOrder);
|
||||
lieGhostText = CreateScreenText("LieKeywordGhost", 4.1f, screenTextSortingOrder - 1);
|
||||
systemText = CreateScreenText("SystemPrompt", 1.35f, screenTextSortingOrder + 1);
|
||||
actorFlashText = CreateScreenText("ActorFlashKeyword", 10.5f, screenTextSortingOrder);
|
||||
actorFlashChromaRed = CreateScreenText("ActorFlashChromaRed", 10.5f, screenTextSortingOrder - 1);
|
||||
actorFlashChromaCyan = CreateScreenText("ActorFlashChromaCyan", 10.5f, screenTextSortingOrder - 1);
|
||||
truthFlashText = CreateScreenText("ActorTruthFlash", 8.5f, screenTextSortingOrder - 1);
|
||||
lieClipper = lieText.gameObject.AddComponent<TMPRectClipper>();
|
||||
lieGhostClipper = lieGhostText.gameObject.AddComponent<TMPRectClipper>();
|
||||
systemClipper = systemText.gameObject.AddComponent<TMPRectClipper>();
|
||||
actorFlashClipper = actorFlashText.gameObject.AddComponent<TMPRectClipper>();
|
||||
actorFlashChromaRedClipper = actorFlashChromaRed.gameObject.AddComponent<TMPRectClipper>();
|
||||
actorFlashChromaCyanClipper = actorFlashChromaCyan.gameObject.AddComponent<TMPRectClipper>();
|
||||
truthFlashClipper = truthFlashText.gameObject.AddComponent<TMPRectClipper>();
|
||||
lieClipper.Initialize(lieText, expressionScreenMaskRect);
|
||||
lieGhostClipper.Initialize(lieGhostText, expressionScreenMaskRect);
|
||||
systemClipper.Initialize(systemText, expressionScreenMaskRect);
|
||||
actorFlashClipper.Initialize(actorFlashText, expressionScreenMaskRect);
|
||||
actorFlashChromaRedClipper.Initialize(actorFlashChromaRed, expressionScreenMaskRect);
|
||||
actorFlashChromaCyanClipper.Initialize(actorFlashChromaCyan, expressionScreenMaskRect);
|
||||
@@ -2437,15 +2223,12 @@ namespace AibisDream.MiniGame.Language
|
||||
PositionText(actorFlashChromaRed, center, screen.size);
|
||||
PositionText(actorFlashChromaCyan, center, screen.size);
|
||||
PositionText(truthFlashText, center, screen.size);
|
||||
Vector3 systemPosition = center + Vector3.up * screen.extents.y * 0.70f;
|
||||
PositionText(systemText, systemPosition, new Vector2(screen.size.x, screen.size.y * 0.22f));
|
||||
}
|
||||
|
||||
private void EnsureScreenTextClipMaterials()
|
||||
{
|
||||
lieClipper?.Initialize(lieText, expressionScreenMaskRect);
|
||||
lieGhostClipper?.Initialize(lieGhostText, expressionScreenMaskRect);
|
||||
systemClipper?.Initialize(systemText, expressionScreenMaskRect);
|
||||
actorFlashClipper?.Initialize(actorFlashText, expressionScreenMaskRect);
|
||||
actorFlashChromaRedClipper?.Initialize(actorFlashChromaRed, expressionScreenMaskRect);
|
||||
actorFlashChromaCyanClipper?.Initialize(actorFlashChromaCyan, expressionScreenMaskRect);
|
||||
@@ -2458,7 +2241,6 @@ namespace AibisDream.MiniGame.Language
|
||||
{
|
||||
lieClipper?.ReleaseMaterial();
|
||||
lieGhostClipper?.ReleaseMaterial();
|
||||
systemClipper?.ReleaseMaterial();
|
||||
actorFlashClipper?.ReleaseMaterial();
|
||||
actorFlashChromaRedClipper?.ReleaseMaterial();
|
||||
actorFlashChromaCyanClipper?.ReleaseMaterial();
|
||||
@@ -2751,22 +2533,6 @@ namespace AibisDream.MiniGame.Language
|
||||
screenDimRenderer.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void SetSystemMessage(string value, Color color, float fadeDuration)
|
||||
{
|
||||
SetTextActive(systemText, value, color);
|
||||
SetTextAlpha(systemText, 0f);
|
||||
FadeText(systemText, 1f, fadeDuration);
|
||||
}
|
||||
|
||||
private void HideSystemMessageImmediate()
|
||||
{
|
||||
if (systemText == null)
|
||||
return;
|
||||
systemText.DOKill();
|
||||
systemText.text = string.Empty;
|
||||
systemText.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void HideLieLayersImmediate()
|
||||
{
|
||||
StopLieShuffleImmediate(false);
|
||||
@@ -2777,7 +2543,6 @@ namespace AibisDream.MiniGame.Language
|
||||
|
||||
private void HideActorFlashImmediate()
|
||||
{
|
||||
StopActorFlashLoopImmediate(false);
|
||||
StopActorOverdriveImmediate();
|
||||
StopTruthLieFlicker();
|
||||
actorFlashActive = false;
|
||||
@@ -2885,25 +2650,6 @@ namespace AibisDream.MiniGame.Language
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryResolvePreset(string value, out Preset preset)
|
||||
{
|
||||
switch ((value ?? string.Empty).Trim().ToLowerInvariant())
|
||||
{
|
||||
case "light":
|
||||
preset = LightPreset;
|
||||
return true;
|
||||
case "medium":
|
||||
preset = MediumPreset;
|
||||
return true;
|
||||
case "heavy":
|
||||
preset = HeavyPreset;
|
||||
return true;
|
||||
default:
|
||||
preset = LightPreset;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Preset GetPresetForMemory(MemoryKind kind)
|
||||
{
|
||||
return kind switch
|
||||
@@ -2914,16 +2660,6 @@ namespace AibisDream.MiniGame.Language
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetLeakFragment(MemoryKind kind)
|
||||
{
|
||||
return kind switch
|
||||
{
|
||||
MemoryKind.Office => "笑话",
|
||||
MemoryKind.Sunset => "离开",
|
||||
_ => "不要那样看我"
|
||||
};
|
||||
}
|
||||
|
||||
private bool ExpectState(string command, params PresentationState[] allowed)
|
||||
{
|
||||
for (int i = 0; i < allowed.Length; i++)
|
||||
|
||||
@@ -26,7 +26,7 @@ Floating Count: 100
|
||||
Candidate Count: 50
|
||||
Red Particle Count: 8
|
||||
Connection Distance: 2
|
||||
Target Sentence: "别过来我感觉害怕"
|
||||
Target Sentence: 由启动命令传入,不在 Inspector 中保存测试文案
|
||||
Waveform Amplitude: 0.3
|
||||
Interaction Radius: 0.5
|
||||
```
|
||||
@@ -68,7 +68,7 @@ Interaction Radius: 0.5
|
||||
1. Window → TextMeshPro → Font Asset Creator
|
||||
2. 选择支持中文的字体(如:思源黑体、微软雅黑)
|
||||
3. Character Set: Custom Characters
|
||||
4. 粘贴`TextParticle.cs`中的`chineseChars`字符串
|
||||
4. 粘贴 `Params/hs.exp.pool` 及正式 Express 文案所需字符
|
||||
5. Generate Font Atlas
|
||||
|
||||
### 看不到粒子?
|
||||
|
||||
@@ -50,7 +50,7 @@ Language/
|
||||
|
||||
#### 游戏参数
|
||||
- **Connection Distance**: 连接距离阈值(默认2,Unity单位)
|
||||
- **Target Sentence**: 目标句子(默认"别过来我感觉害怕")
|
||||
- **Target Sentence**: 目标句子(必须由启动命令传入)
|
||||
|
||||
#### 波形参数
|
||||
- **Waveform Amplitude**: 波形振幅(默认0.3)
|
||||
@@ -91,7 +91,7 @@ Language/
|
||||
完成第一阶段后:
|
||||
1. 红色粒子自动排列成水平一行
|
||||
2. 蓝色粒子淡出
|
||||
3. 红色粒子逐个揭示目标文字:"别过来我感觉害怕"
|
||||
3. 红色粒子逐个揭示启动命令传入的目标文字
|
||||
4. 文字出现剧烈抖动(焦虑效果)
|
||||
|
||||
### 第三阶段:波形交互
|
||||
@@ -158,15 +158,12 @@ Language/
|
||||
|
||||
### 添加新的文字内容
|
||||
|
||||
修改`CandidateParticle.cs`中的字符集:
|
||||
```csharp
|
||||
protected static string chineseChars = "你的字符集...";
|
||||
protected static string[] chineseWords = { "词组1", "词组2", ... };
|
||||
```
|
||||
在 Unity Localization 的 `Params/hs.exp.pool` 中配置当前语言的随机字符池。
|
||||
字符池按 Unicode 文本元素拆分,忽略空白;重复字符可用于提高出现权重。
|
||||
|
||||
### 修改目标句子
|
||||
|
||||
在Manager的Inspector中修改`Target Sentence`字段
|
||||
修改 `ExpressionContentCatalog` 对应轮次引用的 `Params` 本地化条目。
|
||||
|
||||
### 调整波形特性
|
||||
|
||||
|
||||
@@ -27,7 +27,11 @@ namespace AibisDream.MiniGame.Language
|
||||
|
||||
protected float changeTimer;
|
||||
protected float changeInterval = 1f;
|
||||
protected string overrideCharPool;
|
||||
private static readonly IReadOnlyList<string> EmptyCharacterPool =
|
||||
new List<string>().AsReadOnly();
|
||||
protected IReadOnlyList<string> defaultCharacterPool = EmptyCharacterPool;
|
||||
protected IReadOnlyList<string> interferenceCharacterPool = EmptyCharacterPool;
|
||||
protected IReadOnlyList<string> overrideCharacterPool = EmptyCharacterPool;
|
||||
protected Bounds movementBounds;
|
||||
protected bool useCircularBounds = false;
|
||||
protected Vector3 boundsCenter;
|
||||
@@ -36,13 +40,6 @@ namespace AibisDream.MiniGame.Language
|
||||
private Material customFontMaterial;
|
||||
private TMPRectClipper screenClipper;
|
||||
|
||||
// 字符集
|
||||
protected static string chineseChars = "的一是在不了有和人这中大为上个国我以要他时来用们生到作地于出就分对成会可主发年动同工也能下过子说产种面而方后多定行学法所民得经十三之进着等部度家电力里如水化高自二理起小物现实加量都两体制机当使点从业本去把性好应开它合还因由其些然前外天政四日那社义事平形相全表间样与关各重新线内数正心反你明看原又么利比或但质气第向道命此变条只没结解问意建月公无系军很情者最立代想已通并提直题党程展五果料象员革位入常文总次品式活设及管特件长求老头基资边流路级少图山统接知较将组见计别她手角期根论运农指几九区强放决西被干做必战先回则任取据处队南给色光门即保治北造百规热领七海口东导器压志世金增争济阶油思术极交受联什认六共权收证改清己美再采转更单风切打白教速花带安场身车例真务具万每目至达走积示议声报斗完类八离华名确才科张信马节话米整空元况今集温传土许步群广石记需段研界拉林律叫且究观越织装影算低持音众书布复容儿须际商非验连断深难近矿千周委素技备半办青省列习响约支般史感劳便团往酸历市克何除消构府称太准精值号率族维划选标写存候毛亲快效斯院查江型眼王按格养易置派层片始却专状育厂京识适属圆包火住调满县局照参红细引听该铁价严龙飞";
|
||||
protected static string[] chineseWords = { "不安", "紧张", "焦虑", "担忧", "烦躁", "恐慌", "恐惧", "绝望", "痛苦", "悲伤", "愤怒", "孤独", "无助", "迷茫", "困惑", "压抑", "沉重", "疲惫", "空虚", "失落" };
|
||||
|
||||
// 动态配置的干扰短句(笑话/焦虑等,由 Yarn start_expression 传入,用于非目标粒子字符池)
|
||||
protected static List<string> configuredAnxietyPhrases = new List<string>();
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
if (textMesh == null)
|
||||
@@ -85,7 +82,7 @@ namespace AibisDream.MiniGame.Language
|
||||
SyncLayerToChildren();
|
||||
|
||||
changeTimer = Random.Range(0.5f, 1.5f);
|
||||
currentChar = GetNextDisplayChar();
|
||||
currentChar = string.Empty;
|
||||
UpdateText();
|
||||
// TMP 重建 mesh / SubMesh 后再对齐一次 layer
|
||||
SyncLayerToChildren();
|
||||
@@ -112,7 +109,7 @@ namespace AibisDream.MiniGame.Language
|
||||
// 边界检测
|
||||
CheckBounds();
|
||||
|
||||
// 字符变化:目标粒子用 chineseChars 随机,非目标粒子从干扰短句字符中随机
|
||||
// 字符变化:目标粒子用本轮默认池,非目标粒子从干扰短句字符池中随机
|
||||
changeTimer -= Time.deltaTime;
|
||||
if (changeTimer <= 0)
|
||||
{
|
||||
@@ -209,7 +206,7 @@ namespace AibisDream.MiniGame.Language
|
||||
|
||||
protected string GetRandomChar()
|
||||
{
|
||||
return chineseChars[Random.Range(0, chineseChars.Length)].ToString();
|
||||
return GetRandomPoolElement(defaultCharacterPool);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -218,22 +215,14 @@ namespace AibisDream.MiniGame.Language
|
||||
/// </summary>
|
||||
protected string GetRandomCharFromPhrases()
|
||||
{
|
||||
if (configuredAnxietyPhrases == null || configuredAnxietyPhrases.Count == 0)
|
||||
if (interferenceCharacterPool == null || interferenceCharacterPool.Count == 0)
|
||||
return GetRandomChar();
|
||||
var chars = new List<char>();
|
||||
foreach (var phrase in configuredAnxietyPhrases)
|
||||
{
|
||||
if (string.IsNullOrEmpty(phrase)) continue;
|
||||
foreach (char c in phrase)
|
||||
chars.Add(c);
|
||||
}
|
||||
if (chars.Count == 0) return GetRandomChar();
|
||||
return chars[Random.Range(0, chars.Count)].ToString();
|
||||
return GetRandomPoolElement(interferenceCharacterPool);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取下一次要显示的字符。子类可重写以区分目标粒子与非目标粒子(与红蓝颜色解绑)。
|
||||
/// 默认返回 chineseChars 随机单字。
|
||||
/// 默认从本轮本地化字符池随机返回一个 Unicode 文本元素。
|
||||
/// </summary>
|
||||
protected virtual string GetNextDisplayChar()
|
||||
{
|
||||
@@ -247,27 +236,60 @@ namespace AibisDream.MiniGame.Language
|
||||
/// </summary>
|
||||
public void SetOverrideCharPool(string pool)
|
||||
{
|
||||
overrideCharPool = string.IsNullOrEmpty(pool) ? null : pool;
|
||||
SetOverrideCharacterPool(
|
||||
ExpressionTextTokenizer.GetVisibleElements(pool));
|
||||
}
|
||||
|
||||
public void SetOverrideCharacterPool(IReadOnlyList<string> pool)
|
||||
{
|
||||
overrideCharacterPool = pool != null && pool.Count > 0
|
||||
? pool
|
||||
: EmptyCharacterPool;
|
||||
}
|
||||
|
||||
protected bool TryGetOverridePoolChar(out string character)
|
||||
{
|
||||
if (string.IsNullOrEmpty(overrideCharPool))
|
||||
if (!HasOverrideCharacterPool)
|
||||
{
|
||||
character = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
character = overrideCharPool[Random.Range(0, overrideCharPool.Length)].ToString();
|
||||
character = GetRandomPoolElement(overrideCharacterPool);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置全局干扰短句配置(笑话等,由 LanguageParticleManager 调用)
|
||||
/// 设置本轮默认与干扰字符池。调用方负责传入不可变的本轮快照。
|
||||
/// </summary>
|
||||
public static void SetAnxietyPhrases(List<string> phrases)
|
||||
public void SetCharacterPools(
|
||||
IReadOnlyList<string> defaultPool,
|
||||
IReadOnlyList<string> interferencePool)
|
||||
{
|
||||
configuredAnxietyPhrases = phrases ?? new List<string>();
|
||||
defaultCharacterPool = defaultPool != null && defaultPool.Count > 0
|
||||
? defaultPool
|
||||
: EmptyCharacterPool;
|
||||
interferenceCharacterPool =
|
||||
interferencePool != null && interferencePool.Count > 0
|
||||
? interferencePool
|
||||
: EmptyCharacterPool;
|
||||
}
|
||||
|
||||
public void InitializeRandomCharacter()
|
||||
{
|
||||
currentChar = GetNextDisplayChar();
|
||||
UpdateText();
|
||||
changeTimer = Random.Range(0.5f, 1.5f);
|
||||
}
|
||||
|
||||
protected bool HasOverrideCharacterPool =>
|
||||
overrideCharacterPool != null && overrideCharacterPool.Count > 0;
|
||||
|
||||
private static string GetRandomPoolElement(IReadOnlyList<string> pool)
|
||||
{
|
||||
if (pool == null || pool.Count == 0)
|
||||
return string.Empty;
|
||||
return pool[Random.Range(0, pool.Count)];
|
||||
}
|
||||
|
||||
protected void UpdateText()
|
||||
|
||||
@@ -7,9 +7,9 @@ Localization 的 `Params` String Table Collection 作为唯一数据源。
|
||||
|
||||
- `l10n.xxx` 查询 `Params/xxx`。
|
||||
- 查询使用 `DontUseFallback`,不回退中文或其他 Locale。
|
||||
- 中文表保存源数据;英文、日文表保留同一套 Key,未翻译值留空。
|
||||
- Key 不存在时显示 `⟦key⟧` 并记录包含 Table、Key、Locale 的错误。
|
||||
- Express 必填值解析为空时安全终止命令,不启动粒子或释放演出。
|
||||
- 中文表保存源数据;其他 Locale 表保留同一套 Key,未翻译值留空。
|
||||
- Key 不存在时返回 `⟦key⟧` 并记录包含 Table、Key、Locale 的错误。
|
||||
- Express 必填值解析为空或缺失标记时安全终止命令,不启动粒子或释放演出。
|
||||
- `StreamingAssets/Config/params.csv` 已退出运行链路。
|
||||
- 固定 UI 继续使用 `UIText`;普通 Yarn 对白、Task、教学和角色名不迁入
|
||||
`Params`。
|
||||
@@ -25,8 +25,8 @@ Localization 的 `Params` String Table Collection 作为唯一数据源。
|
||||
| `log2` | `l10n.hs.exp.log2.target` | `l10n.hs.exp.log2.tokens` | 28 |
|
||||
| `log3` | `l10n.hs.exp.log3.target` | `l10n.hs.exp.log3.tokens` | 38 |
|
||||
|
||||
Actor Loop 使用 `l10n.hs.exp.actor.loop`。Round ID 大小写敏感;重复 ID、
|
||||
未知 ID、空引用、非 `l10n.*` 引用或解析后的空值均视为数据错误。
|
||||
Round ID 大小写敏感;重复 ID、未知 ID、空引用、非 `l10n.*` 引用或解析
|
||||
后的空值均视为数据错误。
|
||||
|
||||
正式 Yarn 使用:
|
||||
|
||||
@@ -40,8 +40,8 @@ Actor Loop 使用 `l10n.hs.exp.actor.loop`。Round ID 大小写敏感;重复 I
|
||||
|
||||
## Locale 生命周期
|
||||
|
||||
启动轮次时捕获当前 Locale,目标句和 token 并行解析;校验通过后才初始化
|
||||
粒子系统。该 Locale 快照会保留到下一轮开始或 ExpressSystem 关闭,当前轮
|
||||
启动轮次时捕获当前 Locale,目标句、token 和 `l10n.hs.exp.pool` 并行解析;
|
||||
校验通过后才初始化粒子系统。该 Locale 快照会保留到下一轮开始或 ExpressSystem 关闭,当前轮
|
||||
后续释放演出都使用同一快照。没有活动轮次时,演出命令使用命令调用时的
|
||||
当前 Locale。
|
||||
|
||||
@@ -53,9 +53,9 @@ Actor Loop 使用 `l10n.hs.exp.actor.loop`。Round ID 大小写敏感;重复 I
|
||||
Express 使用短前缀 `hs.exp.*`:
|
||||
|
||||
- 三轮目标和 token:`hs.exp.log{1..3}.target`、`.tokens`
|
||||
- Actor Loop:`hs.exp.actor.loop`
|
||||
- LOG3 攻击词:`hs.exp.atk.*`
|
||||
- 最终稳定文字:`hs.exp.final.win`
|
||||
- 全局随机字符池:`hs.exp.pool`
|
||||
|
||||
重复演出复用同一 Key,但 Yarn 中仍保留原有调用次数、等待、数值、顺序和
|
||||
`#line:` 标签。
|
||||
@@ -74,9 +74,10 @@ Express 使用短前缀 `hs.exp.*`:
|
||||
最终排列保留词间距。目标粒子数量、最终排列、攻击词长度校验和 truth leak
|
||||
匹配使用同一套文本元素结果。
|
||||
|
||||
本次明确不修改 `TextParticle` 的随机字符池、`chineseChars`、
|
||||
`chineseWords`、目标粒子入场阶段的随机字符来源,以及背景/干扰粒子的字符
|
||||
池策略。
|
||||
随机字符池同样按 Unicode 文本元素处理。`hs.exp.pool` 是连续文本,不使用
|
||||
`|` 或全角 `|`,空白被忽略,重复元素保留为随机权重。目标粒子从该池取字,
|
||||
非目标和背景浮动粒子继续从本轮 token 展开的干扰池取字;临时老虎机池结束
|
||||
后按粒子角色恢复。旧的 `chineseChars` 与 `chineseWords` 运行时常量均已删除。
|
||||
|
||||
## 内容校验
|
||||
|
||||
@@ -85,8 +86,10 @@ Express 使用短前缀 `hs.exp.*`:
|
||||
- Stage6 正式 Express 文字参数不得残留中文硬编码;
|
||||
- Yarn 和 Catalog 中引用的 `l10n.*` Key 必须存在于 `Params` Shared Data;
|
||||
- 中文值必须非空;
|
||||
- 英文、日文空值只报告待翻译,不导致校验失败;
|
||||
- 未翻译 Locale 的空值只报告待翻译,不导致校验失败;
|
||||
- `.tokens` 至少包含一个有效项,且不能使用全角 `|`;
|
||||
- `hs.exp.pool` 必须存在于所有 Params Locale 表;中文非空,非空译文不得
|
||||
包含 `|`、`|` 或只有空白;
|
||||
- completion node、preset、memory key、颜色、Timeline 等结构参数不误报。
|
||||
|
||||
运行时和 EditMode 测试同时覆盖原始字符串、空值不回退、缺 Key 占位、
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
`Stage6.yarn` 已改为使用原子命令编排。除特别标记为“等待”的命令外,命令只启动效果并立即让 Yarn 继续;并行效果之间的节奏用 `<<wait 秒数>>` 调整。
|
||||
|
||||
旧命令 `expression_lie_begin`、`expression_lie_break`、`expression_truth_reveal` 仍然保留,供其他节点兼容使用。Stage6 不再依赖它们。
|
||||
|
||||
## 火山脸与 LOG1 大字
|
||||
|
||||
| 命令 | 参数 | 行为 |
|
||||
@@ -11,8 +9,6 @@
|
||||
| `expression_face_fade_in` | `duration` | 显式淡入火山脸并等待完成。三条 LOG 都在完成节点中调用,不再由粒子系统自动淡入。 |
|
||||
| `expression_actor_flash` | `"text" holdDuration` | 清晰的大字重击;连续调用会逐级增强,但每次结束都退回晃动粒子字。 |
|
||||
| `expression_actor_flash_in` | 与 `expression_actor_flash` 相同 | 阻塞播放相同的大字重击入场,但保持最后一帧、不退场;用于段落结尾定格。 |
|
||||
| `expression_actor_flash_loop_start` | 无 | 非阻塞启动“没问题 → 冇问题 → 帽问题”三拍无限循环,参数固定为 LOG1 当前节奏。 |
|
||||
| `expression_actor_flash_loop_stop` | 无 | 立即停止循环、清掉当前大字并恢复晃动粒子字。 |
|
||||
| `expression_actor_slot` | `"characterPool" duration interval fontSize "truthPool"` | 中央大字持续换字。`truthPool` 非空时其字符也混入换字池,并在主字上以真话暖色(`truthAccentColor`)显示——真假字符交替闪烁即"争夺"。命令不等待,节奏由紧随其后的同长度 `wait` 控制;结束后停在最后一帧。 |
|
||||
| `expression_actor_truth_flash` | `"text" holdDuration fontSize alpha jitter` | 真话闪现(等待):谎话大字之间,暖色完整真话带细微抖动挣扎浮现一拍。不推进谎话冲击等级、不累积记忆损伤;"被掐断"由紧随其后的 `expression_glitch_pulse` 表现。 |
|
||||
| `expression_actor_word_hit` | `"text" holdDuration` | 单字或短词独占屏幕重击,并等待该拍完成。 |
|
||||
|
||||
Reference in New Issue
Block a user