From 223bcc8c61aa54bcc58ed4b550bad82221c1ed7e Mon Sep 17 00:00:00 2001 From: bottlefish <781230111@qq.com> Date: Mon, 27 Jul 2026 22:48:18 +0800 Subject: [PATCH] =?UTF-8?q?fix(huoshan):=20=E4=BF=AE=E5=A4=8D=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E6=80=81=E6=96=87=E5=AD=97=E7=B2=92=E5=AD=90=E8=B4=B4?= =?UTF-8?q?=E8=BE=B9=E5=87=BA=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../Language/LanguageParticleManager.cs | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs index fb4d262ad..2b72b4270 100644 --- a/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs +++ b/Assets/Scripts/MiniGame/HuoShan/Language/LanguageParticleManager.cs @@ -125,9 +125,9 @@ namespace AibisDream.MiniGame.Language [Tooltip("可选:场景中一块与屏幕内框对齐的 RectTransform(World Space Canvas 子物体即可)。不指定则用粒子用的 movementBounds(canvasSize - textMargin)")] [SerializeField] private RectTransform completionScreenClipRect; [Tooltip("在裁剪区基础上再向内缩进的世界单位距离,避免放大后的字半个挂在边外")] - [SerializeField] private float completionClipEdgePadding = 0.2f; + [SerializeField] private float completionClipEdgePadding = 0.35f; [Tooltip("屏幕内容的最小安全边距;最终边距还会根据字形、光晕、圆环和晃动幅度动态增大。")] - [SerializeField, Min(0f)] private float minimumScreenMargin = 0.12f; + [SerializeField, Min(0f)] private float minimumScreenMargin = 0.2f; [Tooltip("将目标粒子拉回屏幕裁剪区时的平滑时间(越大越柔和,略增加拖尾感)")] [SerializeField, Range(0.02f, 0.55f)] private float completionPositionSmoothTime = 0.2f; @@ -1051,24 +1051,30 @@ namespace AibisDream.MiniGame.Language { float padding = Mathf.Max(minimumScreenMargin, completionClipEdgePadding); float glyphExtent = 0f; + float maxScale = 1f; foreach (CandidateParticle particle in targetParticles) { if (particle == null) continue; + maxScale = Mathf.Max(maxScale, particle.transform.localScale.x, particle.transform.localScale.y); + if (particle.textMesh != null && particle.textMesh.renderer != null) { Bounds textBounds = particle.textMesh.renderer.bounds; glyphExtent = Mathf.Max(glyphExtent, textBounds.extents.x, textBounds.extents.y); } - } - float zoomedGlyph = glyphExtent * Mathf.Max(1f, focusZoomScale); - float shakeExtent = Mathf.Max(shuffleDistance, nonTargetShakeIntensity, completionPositionSmoothTime * 0.1f); + // renderer.bounds 已含当前缩放;未放大到 focusZoom 时再预留缩放余量,避免重复乘 zoom + float resolvedGlyph = maxScale >= focusZoomScale * 0.9f + ? glyphExtent + : glyphExtent * Mathf.Max(1f, focusZoomScale); + // 抖动/漂浮是局部偏移,硬钳制根节点后仍可能露出字形一角,留一点余量即可 + float floatAllowance = Mathf.Max(0.04f, focusInterferenceShakeAmplitude * 0.35f); - float horizontal = Mathf.Max(padding, zoomedGlyph, shakeExtent); + float horizontal = padding + resolvedGlyph + floatAllowance; return new Vector2(horizontal, horizontal); } @@ -1635,8 +1641,10 @@ namespace AibisDream.MiniGame.Language Mathf.Lerp(1f, 0.12f, gather); Vector3 edgeJitter = CalculateLieEdgeJitter(visual, compression); visual.scatterOffset = Vector3.Lerp(visual.scatterOffset, Vector3.zero, Time.deltaTime * 5.5f); - visual.particle.transform.position = - compressedPosition + shuffleOffset + edgeJitter + visual.scatterOffset; + // 硬钳制:shuffle/jitter 会顶出裁剪区,仅靠 LateUpdate SmoothDamp 追不上,字会贴边甚至出屏 + visual.particle.transform.position = ClampPositionToBounds( + compressedPosition + shuffleOffset + edgeJitter + visual.scatterOffset, + GetCompletionClipBounds()); // 锁字判定用原始进度:target=0.92 时阈值 0.95 的最后一个字永远锁不住 UpdateTruthGatherLock(visual, truthGatherProgress, visualCount); @@ -1843,7 +1851,9 @@ namespace AibisDream.MiniGame.Language Vector3 dynamicTarget = GetDynamicArrangementTarget(visual, arrangementProgress, localTime); Vector3 targetPosition = Vector3.Lerp(visual.focusPosition, dynamicTarget, Mathf.SmoothStep(0f, 1f, arrangementProgress)); Vector3 shuffleOffset = CalculateShuffleOffset(visual, arrangementProgress, localTime); - visual.particle.transform.position = targetPosition + shuffleOffset; + visual.particle.transform.position = ClampPositionToBounds( + targetPosition + shuffleOffset, + GetCompletionClipBounds()); if (!visual.stabilized && probabilityProgress >= 1f) {