diff --git a/Assets/Editor/Huoshan/ExpressionLocalizationTests.cs b/Assets/Editor/Huoshan/ExpressionLocalizationTests.cs index ee9f1087a..130e4984d 100644 --- a/Assets/Editor/Huoshan/ExpressionLocalizationTests.cs +++ b/Assets/Editor/Huoshan/ExpressionLocalizationTests.cs @@ -366,18 +366,13 @@ namespace AibisDream.EditorTests.Huoshan } [Test] - public void ParticleGeometry_UsesVisualEdgeDistanceAndShortestSeparationAxis() + public void ParticleGeometry_UsesShortestSeparationAxisForWordBounds() { var left = new Bounds(Vector3.zero, new Vector3(4f, 1f, 0.01f)); var right = new Bounds( - new Vector3(4.15f, 0f, 0f), + new Vector3(3.8f, 0f, 0f), new Vector3(4f, 1f, 0.01f)); - Assert.That( - ExpressionParticleGeometry.BoundsDistance(left, right), - Is.EqualTo(0.15f).Within(0.0001f)); - - right.center = new Vector3(3.8f, 0f, 0f); Assert.That( ExpressionParticleGeometry.TryGetSeparation( left, diff --git a/Assets/GameContent/Feature_Huoshan/Expression/ExpressionParticleLanguageProfile.asset b/Assets/GameContent/Feature_Huoshan/Expression/ExpressionParticleLanguageProfile.asset index 7a159a450..b87bc5e74 100644 --- a/Assets/GameContent/Feature_Huoshan/Expression/ExpressionParticleLanguageProfile.asset +++ b/Assets/GameContent/Feature_Huoshan/Expression/ExpressionParticleLanguageProfile.asset @@ -20,7 +20,6 @@ MonoBehaviour: floatingFontScale: 1 nonTargetCountScale: 1 wordSpacing: 0.18 - visualEdgeConnectionDistance: 0 minimumReadablePhraseScale: 0.65 - localeCode: ja-JP unitMode: 0 @@ -29,7 +28,6 @@ MonoBehaviour: floatingFontScale: 1 nonTargetCountScale: 1 wordSpacing: 0.18 - visualEdgeConnectionDistance: 0 minimumReadablePhraseScale: 0.65 - localeCode: en unitMode: 1 @@ -38,7 +36,6 @@ MonoBehaviour: floatingFontScale: 0.7 nonTargetCountScale: 0.75 wordSpacing: 0.18 - visualEdgeConnectionDistance: 0.18 minimumReadablePhraseScale: 0.65 - localeCode: es unitMode: 1 @@ -47,7 +44,6 @@ MonoBehaviour: floatingFontScale: 0.7 nonTargetCountScale: 0.75 wordSpacing: 0.18 - visualEdgeConnectionDistance: 0.18 minimumReadablePhraseScale: 0.65 - localeCode: ru unitMode: 1 @@ -56,7 +52,6 @@ MonoBehaviour: floatingFontScale: 0.7 nonTargetCountScale: 0.75 wordSpacing: 0.18 - visualEdgeConnectionDistance: 0.18 minimumReadablePhraseScale: 0.65 - localeCode: pt-BR unitMode: 1 @@ -65,5 +60,4 @@ MonoBehaviour: floatingFontScale: 0.7 nonTargetCountScale: 0.75 wordSpacing: 0.18 - visualEdgeConnectionDistance: 0.18 minimumReadablePhraseScale: 0.65 diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/ConnectionRenderer.cs b/Assets/Scripts/MiniGame/HuoShan/Language/ConnectionRenderer.cs index c673be267..6a39933bf 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/ConnectionRenderer.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/ConnectionRenderer.cs @@ -133,20 +133,15 @@ namespace AibisDream.MiniGame.Language // 跳过红色粒子 if (p1.isRed && p2.isRed) continue; - if (!p1.connections.Contains(p2)) - continue; + float distance = Vector3.Distance(p1.transform.position, p2.transform.position); + if (distance < connectionDistance) + { + float alpha = Mathf.Lerp(0.7f, 0f, distance / connectionDistance) * fadeOutAlpha; + Color lineColor = nonTargetConnectionColor; + lineColor.a = alpha * nonTargetAlphaScale * nonTargetConnectionColor.a; - ResolveConnectionVisual( - p1, - p2, - out Vector3 from, - out Vector3 to, - out float proximity); - float alpha = Mathf.Lerp(0f, 0.7f, proximity) * fadeOutAlpha; - Color lineColor = nonTargetConnectionColor; - lineColor.a = alpha * nonTargetAlphaScale * nonTargetConnectionColor.a; - - DrawClippedLine(from, to, lineColor); + DrawClippedLine(p1.transform.position, p2.transform.position, lineColor); + } } } } @@ -167,50 +162,23 @@ namespace AibisDream.MiniGame.Language { for (int j = i + 1; j < redParticles.Count; j++) { - CandidateParticle first = redParticles[i]; - CandidateParticle second = redParticles[j]; - if (!first.connections.Contains(second)) - continue; + float distance = Vector3.Distance( + redParticles[i].transform.position, + redParticles[j].transform.position + ); - ResolveConnectionVisual( - first, - second, - out Vector3 from, - out Vector3 to, - out float proximity); - float alpha = Mathf.Lerp(0.4f, 1f, proximity); - Color lineColor = targetConnectionColor; - lineColor.a = alpha * targetAlphaScale * targetConnectionColor.a; + if (distance < connectionDistance) + { + float alpha = Mathf.Lerp(1f, 0.4f, distance / connectionDistance); + Color lineColor = targetConnectionColor; + lineColor.a = alpha * targetAlphaScale * targetConnectionColor.a; - DrawClippedLine(from, to, lineColor); + DrawClippedLine(redParticles[i].transform.position, redParticles[j].transform.position, lineColor); + } } } } - private void ResolveConnectionVisual( - CandidateParticle first, - CandidateParticle second, - out Vector3 from, - out Vector3 to, - out float proximity) - { - if (manager != null && - manager.TryGetConnectionVisual( - first, - second, - out from, - out to, - out proximity)) - { - return; - } - - from = first.transform.position; - to = second.transform.position; - float distance = Vector3.Distance(from, to); - proximity = 1f - Mathf.Clamp01(distance / Mathf.Max(0.0001f, connectionDistance)); - } - private void DrawPropagationEffects() { Draw.LineGeometry = LineGeometry.Flat2D; diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionParticleGeometry.cs b/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionParticleGeometry.cs index 2854bc9fe..18211b41f 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionParticleGeometry.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionParticleGeometry.cs @@ -4,67 +4,6 @@ namespace AibisDream.MiniGame.Language { public static class ExpressionParticleGeometry { - public static bool TryGetConnection( - TextParticle first, - TextParticle second, - float centerDistanceLimit, - float edgeDistanceLimit, - out Vector3 from, - out Vector3 to, - out float proximity) - { - from = first != null ? first.transform.position : Vector3.zero; - to = second != null ? second.transform.position : Vector3.zero; - proximity = 0f; - if (first == null || second == null) - return false; - - float centerDistance = Vector3.Distance( - first.transform.position, - second.transform.position); - bool centerConnected = - centerDistanceLimit > 0f && centerDistance < centerDistanceLimit; - - Bounds firstBounds = first.GetVisualWorldBounds(); - Bounds secondBounds = second.GetVisualWorldBounds(); - float edgeDistance = BoundsDistance(firstBounds, secondBounds); - bool edgeConnected = - edgeDistanceLimit > 0f && edgeDistance <= edgeDistanceLimit; - - if (!centerConnected && !edgeConnected) - return false; - - from = firstBounds.ClosestPoint(secondBounds.center); - to = secondBounds.ClosestPoint(firstBounds.center); - if ((to - from).sqrMagnitude < 0.000001f) - { - from = first.transform.position; - to = second.transform.position; - } - - float centerProximity = centerConnected - ? 1f - Mathf.Clamp01(centerDistance / centerDistanceLimit) - : 0f; - float edgeProximity = edgeConnected - ? 1f - Mathf.Clamp01(edgeDistance / Mathf.Max(0.0001f, edgeDistanceLimit)) - : 0f; - proximity = Mathf.Max(centerProximity, edgeProximity); - return true; - } - - public static float BoundsDistance(Bounds first, Bounds second) - { - float dx = Mathf.Max( - first.min.x - second.max.x, - second.min.x - first.max.x, - 0f); - float dy = Mathf.Max( - first.min.y - second.max.y, - second.min.y - first.max.y, - 0f); - return Mathf.Sqrt(dx * dx + dy * dy); - } - public static bool TryGetSeparation( Bounds first, Bounds second, diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionParticleLanguageProfile.cs b/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionParticleLanguageProfile.cs index 8d5ee0022..b4e8f4456 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionParticleLanguageProfile.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/ExpressionParticleLanguageProfile.cs @@ -22,7 +22,6 @@ namespace AibisDream.MiniGame.Language [SerializeField, Min(0.1f)] private float floatingFontScale = 1f; [SerializeField, Min(0f)] private float nonTargetCountScale = 1f; [SerializeField, Min(0f)] private float wordSpacing = 0.18f; - [SerializeField, Min(0f)] private float visualEdgeConnectionDistance = 0f; [SerializeField, Range(0.1f, 1f)] private float minimumReadablePhraseScale = 0.65f; public string LocaleCode => localeCode; @@ -32,7 +31,6 @@ namespace AibisDream.MiniGame.Language public float FloatingFontScale => floatingFontScale; public float NonTargetCountScale => nonTargetCountScale; public float WordSpacing => wordSpacing; - public float VisualEdgeConnectionDistance => visualEdgeConnectionDistance; public float MinimumReadablePhraseScale => minimumReadablePhraseScale; public int ScaleNonTargetCount(int baseCount) diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs index e34b6fdd7..494b32e53 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs @@ -1198,12 +1198,12 @@ namespace AibisDream.MiniGame.Language continue; } - if (TryGetConnectionVisual( - candidateParticles[i], - candidateParticles[j], - out _, - out _, - out _)) + float distance = Vector3.Distance( + candidateParticles[i].transform.position, + candidateParticles[j].transform.position + ); + + if (distance < connectionDistance) { candidateParticles[i].connections.Add(candidateParticles[j]); candidateParticles[j].connections.Add(candidateParticles[i]); @@ -1219,12 +1219,12 @@ namespace AibisDream.MiniGame.Language { for (int j = i + 1; j < candidateParticles.Count; j++) { - if (TryGetConnectionVisual( - candidateParticles[i], - candidateParticles[j], - out _, - out _, - out _)) + float distance = Vector3.Distance( + candidateParticles[i].transform.position, + candidateParticles[j].transform.position + ); + + if (distance < connectionDistance) { string key = $"{Mathf.Min(i, j)}-{Mathf.Max(i, j)}"; previousConnections[key] = true; @@ -1241,12 +1241,12 @@ namespace AibisDream.MiniGame.Language { for (int j = i + 1; j < candidateParticles.Count; j++) { - if (TryGetConnectionVisual( - candidateParticles[i], - candidateParticles[j], - out _, - out _, - out _)) + float distance = Vector3.Distance( + candidateParticles[i].transform.position, + candidateParticles[j].transform.position + ); + + if (distance < connectionDistance) { string key = $"{Mathf.Min(i, j)}-{Mathf.Max(i, j)}"; currentConnections[key] = true; @@ -1291,7 +1291,10 @@ namespace AibisDream.MiniGame.Language { if (other.isRed) continue; - if (red.connections.Contains(other)) + float distance = Vector3.Distance( + red.transform.position, + other.transform.position); + if (distance < connectionDistance) return false; } } @@ -1299,27 +1302,6 @@ namespace AibisDream.MiniGame.Language return true; } - public bool TryGetConnectionVisual( - CandidateParticle first, - CandidateParticle second, - out Vector3 from, - out Vector3 to, - out float proximity) - { - float edgeDistance = - ActiveUnitMode == ExpressionParticleUnitMode.Word - ? activeSnapshot?.Profile?.VisualEdgeConnectionDistance ?? 0.18f - : 0f; - return ExpressionParticleGeometry.TryGetConnection( - first, - second, - connectionDistance, - edgeDistance, - out from, - out to, - out proximity); - } - private void OnTargetsCompleted() { completionPhase = CompletionPhase.Completed; diff --git a/Assets/Tests/Huoshan.meta b/Assets/Tests/Huoshan.meta deleted file mode 100644 index 43cc31015..000000000 --- a/Assets/Tests/Huoshan.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 84ac13641a36486d8f1a891bad6b1f28 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Huoshan/PlayMode.meta b/Assets/Tests/Huoshan/PlayMode.meta deleted file mode 100644 index fd250017c..000000000 --- a/Assets/Tests/Huoshan/PlayMode.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: f6244beee7e24912bd0e6019ac802f92 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Huoshan/PlayMode/AibisDream.Huoshan.Tests.PlayMode.asmdef b/Assets/Tests/Huoshan/PlayMode/AibisDream.Huoshan.Tests.PlayMode.asmdef deleted file mode 100644 index 2268144d7..000000000 --- a/Assets/Tests/Huoshan/PlayMode/AibisDream.Huoshan.Tests.PlayMode.asmdef +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "AibisDream.Huoshan.Tests.PlayMode", - "rootNamespace": "AibisDream.Huoshan.Tests.PlayMode", - "references": [], - "includePlatforms": [], - "excludePlatforms": [], - "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": false, - "defineConstraints": [ - "UNITY_INCLUDE_TESTS" - ], - "versionDefines": [], - "noEngineReferences": false, - "optionalUnityReferences": [ - "TestAssemblies" - ] -} diff --git a/Assets/Tests/Huoshan/PlayMode/AibisDream.Huoshan.Tests.PlayMode.asmdef.meta b/Assets/Tests/Huoshan/PlayMode/AibisDream.Huoshan.Tests.PlayMode.asmdef.meta deleted file mode 100644 index 8b599d43f..000000000 --- a/Assets/Tests/Huoshan/PlayMode/AibisDream.Huoshan.Tests.PlayMode.asmdef.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 6ab7be47fb2561242a443b00c0089c26 -AssemblyDefinitionImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Tests/Huoshan/PlayMode/ExpressionParticlePlayModeTests.cs b/Assets/Tests/Huoshan/PlayMode/ExpressionParticlePlayModeTests.cs deleted file mode 100644 index 8d51bcfea..000000000 --- a/Assets/Tests/Huoshan/PlayMode/ExpressionParticlePlayModeTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections; -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"; - - [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); - } - } -} diff --git a/Assets/Tests/Huoshan/PlayMode/ExpressionParticlePlayModeTests.cs.meta b/Assets/Tests/Huoshan/PlayMode/ExpressionParticlePlayModeTests.cs.meta deleted file mode 100644 index 9a2576074..000000000 --- a/Assets/Tests/Huoshan/PlayMode/ExpressionParticlePlayModeTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 26043594097949158ba1572f70c94f78 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Docs/HuoShan/HuoshanStage6LocalizationPlan.md b/Docs/HuoShan/HuoshanStage6LocalizationPlan.md index 7d5fb8f9e..9812dcad9 100644 --- a/Docs/HuoShan/HuoshanStage6LocalizationPlan.md +++ b/Docs/HuoShan/HuoshanStage6LocalizationPlan.md @@ -89,8 +89,9 @@ truth leak 和老虎机覆盖池都使用同一份快照或同一 Unit Mode, 粒子角色恢复。 Word Profile 还会缩小三类字号、降低非目标粒子数量,并启用宽度感知排版、 -字形边缘连接、Bounds 分离、屏幕边界和鼠标命中。全屏 Actor 故障/老虎机演出 -仍保持原来的字符级视觉。 +Bounds 分离、屏幕边界和鼠标命中。粒子连接仍统一使用原有的中心距离判定和 +中心到中心绘制,不受文字内容或字形宽度影响。全屏 Actor 故障/老虎机演出仍 +保持原来的字符级视觉。 ## 内容校验 @@ -107,6 +108,6 @@ Word Profile 还会缩小三类字号、降低非目标粒子数量,并启用 包含 `|`、`|` 或只有空白,并按 Locale 模式验证; - completion node、preset、memory key、颜色、Timeline 等结构参数不误报。 -EditMode 与 PlayMode 测试覆盖原始字符串、空值不回退、缺 Key 占位、显式 -Locale、轮次配置、Locale/Profile 解析、Word/Grapheme 切分、快照、粒子 -数量倍率,以及真实粒子组件的 Bounds 命中和字形边缘连接行为。 +EditMode 测试覆盖原始字符串、空值不回退、缺 Key 占位、显式 Locale、轮次 +配置、Locale/Profile 解析、Word/Grapheme 切分、快照、粒子数量倍率,以及 +真实粒子组件的 Bounds 命中和宽度感知分离行为。