fix(huoshan): 修复完成态文字粒子贴边出屏

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-27 22:48:18 +08:00
co-authored by Cursor
parent 65735dec52
commit 223bcc8c61
@@ -125,9 +125,9 @@ namespace AibisDream.MiniGame.Language
[Tooltip("可选:场景中一块与屏幕内框对齐的 RectTransformWorld Space Canvas 子物体即可)。不指定则用粒子用的 movementBoundscanvasSize - 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)
{