Skip to content

Commit eca2699

Browse files
committed
init
0 parents  commit eca2699

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1692
-0
lines changed

.gitignore

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# This .gitignore file should be placed at the root of your Unity project directory
2+
#
3+
# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore
4+
#
5+
/[Ll]ibrary/
6+
/[Tt]emp/
7+
/[Oo]bj/
8+
/[Bb]uild/
9+
/[Bb]uilds/
10+
/[Ll]ogs/
11+
/[Uu]ser[Ss]ettings/
12+
13+
# MemoryCaptures can get excessive in size.
14+
# They also could contain extremely sensitive data
15+
/[Mm]emoryCaptures/
16+
17+
# Recordings can get excessive in size
18+
/[Rr]ecordings/
19+
20+
# Uncomment this line if you wish to ignore the asset store tools plugin
21+
# /[Aa]ssets/AssetStoreTools*
22+
23+
# Autogenerated Jetbrains Rider plugin
24+
/[Aa]ssets/Plugins/Editor/JetBrains*
25+
26+
# Visual Studio cache directory
27+
.vs/
28+
29+
# Gradle cache directory
30+
.gradle/
31+
32+
# Autogenerated VS/MD/Consulo solution and project files
33+
ExportedObj/
34+
.consulo/
35+
*.csproj
36+
*.unityproj
37+
*.sln
38+
*.suo
39+
*.tmp
40+
*.user
41+
*.userprefs
42+
*.pidb
43+
*.booproj
44+
*.svd
45+
*.pdb
46+
*.mdb
47+
*.opendb
48+
*.VC.db
49+
50+
# Unity3D generated meta files
51+
*.pidb.meta
52+
*.pdb.meta
53+
*.mdb.meta
54+
55+
# Unity3D generated file on crash reports
56+
sysinfo.txt
57+
58+
# Builds
59+
*.apk
60+
*.aab
61+
*.unitypackage
62+
*.unitypackage.meta
63+
*.app
64+
65+
# Crashlytics generated file
66+
crashlytics-build.properties
67+
68+
# Packed Addressables
69+
/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin*
70+
71+
# Temporary auto-generated Android Assets
72+
/[Aa]ssets/[Ss]treamingAssets/aa.meta
73+
/[Aa]ssets/[Ss]treamingAssets/aa/*

Components.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Components/CharacterDataEntry.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace BP.TMPA
2+
{
3+
internal class CharacterDataEntry
4+
{
5+
public bool IsVisible { get; private set; }
6+
public float FirstVisibleTime { get; private set; }
7+
8+
// Constructor for invisible state
9+
public CharacterDataEntry()
10+
{
11+
IsVisible = false;
12+
FirstVisibleTime = 0f;
13+
}
14+
15+
internal void Update(bool visible, float currentTime)
16+
{
17+
IsVisible = visible;
18+
FirstVisibleTime = currentTime;
19+
}
20+
}
21+
}

Components/CharacterDataEntry.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Components/TextMeshPreprocessor.cs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text.RegularExpressions;
5+
using TMPro;
6+
using UnityEngine.Pool;
7+
8+
namespace BP.TMPA
9+
{
10+
internal class TextMeshPreprocessor : ITextPreprocessor, IDisposable
11+
{
12+
private const string tagPattern = @"<(\/?)([A-Za-z0-9]+)(=[^>]+)?(\/?)>";
13+
14+
// Caching mechanisms
15+
private string lastInputText;
16+
private string processedText;
17+
18+
// Object pooling for TagData
19+
private readonly ObjectPool<TextTagData> tagDataPool;
20+
private readonly ObjectPool<List<TextTagData>> tagListPool;
21+
22+
public Dictionary<int, List<TextTagData>> CharacterTagEffects { get; } = new();
23+
private readonly Func<string, string, bool> tagValidator;
24+
25+
// Reusable collections to reduce allocations
26+
private readonly Dictionary<string, Stack<TextTagData>> activeStacks = new(16);
27+
28+
public TextMeshPreprocessor(Func<string, string, bool> tagValidator)
29+
{
30+
this.tagValidator = tagValidator ?? throw new ArgumentNullException(nameof(tagValidator));
31+
32+
tagDataPool = new ObjectPool<TextTagData>(
33+
createFunc: () => new TextTagData(),
34+
actionOnGet: (tag) => tag.Reset(),
35+
actionOnRelease: (tag) => tag.Clear(),
36+
defaultCapacity: 64
37+
);
38+
39+
tagListPool = new ObjectPool<List<TextTagData>>(
40+
createFunc: () => new List<TextTagData>(),
41+
actionOnGet: (list) => list.Clear(),
42+
defaultCapacity: 64
43+
);
44+
}
45+
46+
public string PreprocessText(string inputText)
47+
{
48+
// Checks if the last processed text really changed
49+
if (inputText != lastInputText)
50+
{
51+
processedText = ProcessText(inputText);
52+
lastInputText = inputText;
53+
}
54+
55+
return processedText;
56+
}
57+
58+
private string ProcessText(string input)
59+
{
60+
ReleaseResources();
61+
CharacterTagEffects.Clear();
62+
activeStacks.Clear();
63+
64+
string processedText = input;
65+
var matches = Regex.Matches(input, tagPattern);
66+
int charIndex = 0;
67+
68+
foreach (Match match in matches.Cast<Match>())
69+
{
70+
// Tag properties
71+
string tag = match.Value;
72+
bool isClosingTag = match.Groups[1].Value == "/";
73+
string tagName = match.Groups[2].Value;
74+
string attributes = match.Groups[3].Value;
75+
int tagIndex = match.Index - charIndex;
76+
77+
if (tagValidator(tagName, attributes))
78+
{
79+
if (isClosingTag)
80+
{
81+
if (!activeStacks.TryGetValue(tagName, out var activeStack))
82+
continue;
83+
84+
var matchingOpenTag = activeStack.Pop();
85+
if (matchingOpenTag != null)
86+
{
87+
matchingOpenTag.Close(tagIndex);
88+
for (int i = matchingOpenTag.StartIndex; i < matchingOpenTag.EndIndex; i++)
89+
{
90+
// Use pooled list for character tag effects
91+
if (!CharacterTagEffects.TryGetValue(i, out var tagList))
92+
{
93+
tagList = tagListPool.Get();
94+
CharacterTagEffects[i] = tagList;
95+
}
96+
97+
// Only add the innermost tag at each position
98+
if (!tagList.Any(t => t.Name == matchingOpenTag.Name))
99+
tagList.Add(matchingOpenTag);
100+
}
101+
}
102+
}
103+
else
104+
{
105+
var tagData = tagDataPool.Get();
106+
tagData.Initialize(tag, tagName, attributes, tagIndex, int.MaxValue);
107+
AddTagToStack(tagName, tagData);
108+
}
109+
110+
// Removes tag from display and offsets the range
111+
processedText = processedText.Replace(tag, string.Empty);
112+
charIndex += tag.Length;
113+
}
114+
}
115+
116+
foreach (var stack in activeStacks)
117+
{
118+
foreach (var tag in stack.Value)
119+
{
120+
for (int i = tag.StartIndex; i < processedText.Length; i++)
121+
{
122+
// Use pooled list for character tag effects
123+
if (!CharacterTagEffects.TryGetValue(i, out var tagList))
124+
{
125+
tagList = tagListPool.Get();
126+
CharacterTagEffects[i] = tagList;
127+
}
128+
129+
if (!tagList.Any(t => t.Name == tag.Name))
130+
tagList.Add(tag);
131+
}
132+
}
133+
}
134+
135+
return processedText;
136+
}
137+
138+
private void AddTagToStack(string name, TextTagData data)
139+
{
140+
if (activeStacks.TryGetValue(name, out var stack))
141+
{
142+
if (stack.Peek().RawTag != data.RawTag)
143+
stack.Push(data);
144+
}
145+
else
146+
{
147+
var newStack = new Stack<TextTagData>();
148+
newStack.Push(data);
149+
activeStacks.Add(name, newStack);
150+
}
151+
}
152+
153+
/// <summary>
154+
/// Releases all tag lists back to the pool.
155+
/// </summary>
156+
private void ReleaseResources()
157+
{
158+
// Release all the tag lists before rebuilding them
159+
foreach (var tagList in CharacterTagEffects.Values)
160+
{
161+
if (tagList != null)
162+
{
163+
tagListPool.Release(tagList);
164+
}
165+
}
166+
167+
// Clear the dictionary as well
168+
CharacterTagEffects.Clear();
169+
}
170+
171+
public List<TextTagData> GetTagEffectsAtIndex(int index)
172+
{
173+
CharacterTagEffects.TryGetValue(index, out var tags);
174+
return tags;
175+
}
176+
177+
public void Dispose()
178+
{
179+
tagDataPool.Clear();
180+
tagListPool.Clear();
181+
CharacterTagEffects.Clear();
182+
}
183+
184+
public void ClearCache()
185+
{
186+
lastInputText = string.Empty;
187+
processedText = string.Empty;
188+
}
189+
}
190+
}

Components/TextMeshPreprocessor.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)