feat: 火山表达维修粒子字符池拆分

This commit is contained in:
2026-07-29 13:00:59 +08:00
parent a48e4babd4
commit 8f2ee603d5
26 changed files with 1830 additions and 401 deletions
@@ -0,0 +1,152 @@
using System;
using System.Collections;
using System.Linq;
using System.Reflection;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace AibisDream.Huoshan.Tests.PlayMode
{
public sealed class ExpressionParticlePlayModeTests
{
private const string RuntimeAssemblyName = "Assembly-CSharp";
[Test]
public void WordMode_TokenizesWholeWordsAtRuntime()
{
Type tokenizerType = RequireRuntimeType(
"AibisDream.MiniGame.Language.ExpressionTextTokenizer");
Type modeType = RequireRuntimeType(
"AibisDream.MiniGame.Language.ExpressionParticleUnitMode");
object wordMode = Enum.Parse(modeType, "Word");
MethodInfo tokenize = tokenizerType.GetMethod(
"TokenizeText",
BindingFlags.Public | BindingFlags.Static);
Assert.That(tokenize, Is.Not.Null);
IEnumerable result = (IEnumerable)tokenize.Invoke(
null,
new[] { "I am not self-doubt", wordMode });
string[] units = result.Cast<object>()
.Select(value => value.ToString())
.ToArray();
Assert.That(
units,
Is.EqualTo(new[] { "I", "am", "not", "self-doubt" }));
}
[UnityTest]
public IEnumerator WordParticle_BoundsIncludesBothPointerEdges()
{
GameObject gameObject = new GameObject("WordParticlePlayModeTest");
try
{
Component particle = gameObject.AddComponent(
RequireRuntimeType(
"AibisDream.MiniGame.Language.CandidateParticle"));
Invoke(particle, "ForceSetUnitText", "self-doubt");
yield return null;
Bounds bounds = (Bounds)Invoke(particle, "GetVisualWorldBounds");
Assert.That(bounds.size.x, Is.GreaterThan(0f));
float leftDistance = (float)Invoke(
particle,
"DistanceToVisualBounds",
new Vector2(bounds.min.x, bounds.center.y));
float rightDistance = (float)Invoke(
particle,
"DistanceToVisualBounds",
new Vector2(bounds.max.x, bounds.center.y));
Assert.That(leftDistance, Is.EqualTo(0f).Within(0.0001f));
Assert.That(rightDistance, Is.EqualTo(0f).Within(0.0001f));
}
finally
{
UnityEngine.Object.Destroy(gameObject);
}
yield return null;
}
[UnityTest]
public IEnumerator LongWords_ConnectThroughVisualEdgeDistance()
{
GameObject firstObject = new GameObject("FirstWordParticle");
GameObject secondObject = new GameObject("SecondWordParticle");
try
{
Type particleType = RequireRuntimeType(
"AibisDream.MiniGame.Language.CandidateParticle");
Component first = firstObject.AddComponent(particleType);
Component second = secondObject.AddComponent(particleType);
Invoke(first, "ForceSetUnitText", "internationalization");
Invoke(second, "ForceSetUnitText", "characteristically");
yield return null;
Bounds firstBounds =
(Bounds)Invoke(first, "GetVisualWorldBounds");
Bounds secondBounds =
(Bounds)Invoke(second, "GetVisualWorldBounds");
secondObject.transform.position = new Vector3(
firstBounds.extents.x + secondBounds.extents.x + 0.1f,
0f,
0f);
Type geometryType = RequireRuntimeType(
"AibisDream.MiniGame.Language.ExpressionParticleGeometry");
MethodInfo connection = geometryType.GetMethod(
"TryGetConnection",
BindingFlags.Public | BindingFlags.Static);
object[] arguments =
{
first,
second,
0.01f,
0.18f,
Vector3.zero,
Vector3.zero,
0f
};
Assert.That(connection, Is.Not.Null);
bool connected = (bool)connection.Invoke(null, arguments);
Assert.That(connected, Is.True);
Assert.That((float)arguments[6], Is.GreaterThan(0f));
Assert.That(
Vector3.Distance(
firstObject.transform.position,
secondObject.transform.position),
Is.GreaterThan(0.01f));
}
finally
{
UnityEngine.Object.Destroy(firstObject);
UnityEngine.Object.Destroy(secondObject);
}
yield return null;
}
private static Type RequireRuntimeType(string fullName)
{
Type type = Type.GetType($"{fullName}, {RuntimeAssemblyName}");
Assert.That(type, Is.Not.Null, fullName);
return type;
}
private static object Invoke(
object target,
string methodName,
params object[] arguments)
{
MethodInfo method = target.GetType().GetMethod(
methodName,
BindingFlags.Public | BindingFlags.Instance);
Assert.That(method, Is.Not.Null, methodName);
return method.Invoke(target, arguments);
}
}
}