Files
aibis-dream/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionTextTokenizer.cs
T
2026-07-28 21:34:42 +08:00

120 lines
3.9 KiB
C#

using System.Collections.Generic;
using System.Globalization;
namespace AibisDream.MiniGame.Language
{
/// <summary>
/// 将玩家可见文字按 Unicode 文本元素拆分。
/// 空白保留在布局中但不生成粒子;随机字符池直接忽略空白。
/// </summary>
public static class ExpressionTextTokenizer
{
public readonly struct Layout
{
public Layout(List<string> visibleElements, List<float> offsets)
{
VisibleElements = visibleElements;
Offsets = offsets;
}
public IReadOnlyList<string> VisibleElements { get; }
public IReadOnlyList<float> Offsets { get; }
public int Count => VisibleElements?.Count ?? 0;
}
public static List<string> GetVisibleElements(string text)
{
var result = new List<string>();
if (string.IsNullOrEmpty(text))
return result;
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(text);
while (enumerator.MoveNext())
{
string element = enumerator.GetTextElement();
if (!string.IsNullOrWhiteSpace(element))
result.Add(element);
}
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,
float whitespaceSpacingMultiplier)
{
var visible = new List<string>();
var positions = new List<float>();
if (string.IsNullOrEmpty(text))
return new Layout(visible, positions);
float spacing = characterSpacing > 0f ? characterSpacing : 1f;
float whitespaceAdvance = spacing * whitespaceSpacingMultiplier;
float cursor = 0f;
TextElementEnumerator enumerator = StringInfo.GetTextElementEnumerator(text);
while (enumerator.MoveNext())
{
string element = enumerator.GetTextElement();
if (string.IsNullOrWhiteSpace(element))
{
if (visible.Count > 0)
cursor += whitespaceAdvance;
continue;
}
visible.Add(element);
positions.Add(cursor);
cursor += spacing;
}
if (positions.Count > 0)
{
float center = (positions[0] + positions[positions.Count - 1]) * 0.5f;
for (int i = 0; i < positions.Count; i++)
positions[i] -= center;
}
return new Layout(visible, positions);
}
public static int FindVisibleSequence(
IReadOnlyList<string> source,
IReadOnlyList<string> fragment)
{
if (source == null || fragment == null || fragment.Count == 0 || fragment.Count > source.Count)
return -1;
int lastStart = source.Count - fragment.Count;
for (int start = 0; start <= lastStart; start++)
{
bool matches = true;
for (int i = 0; i < fragment.Count; i++)
{
if (!string.Equals(source[start + i], fragment[i], System.StringComparison.Ordinal))
{
matches = false;
break;
}
}
if (matches)
return start;
}
return -1;
}
}
}