diff --git a/Assets/Scripts/PlayerVisual.cs b/Assets/Scripts/PlayerVisual.cs index e6123b63a1..f0e21fdb11 100644 --- a/Assets/Scripts/PlayerVisual.cs +++ b/Assets/Scripts/PlayerVisual.cs @@ -24,6 +24,7 @@ namespace BrewMonster [SerializeField] private bool isHit; [SerializeField] private int id; [SerializeField] private bool isDebug; + [SerializeField] private bool debugNamePlateBounds; private bool _eventBusSubscribed; private const float FadeTime = 100; @@ -311,5 +312,73 @@ namespace BrewMonster BMLogger.Log($"PlayerVisual: RefreshNamedAnimancer - Successfully refreshed namedAnimancer from model: {modelRoot?.name ?? "default"}"); } } + + /// + /// Resolve nameplate anchor from merged bounds of all SkinnedMeshRenderers. + /// 从所有SkinnedMeshRenderer合并后的包围盒解析名牌锚点。 + /// + public bool TryGetNamePlateAnchorWorld(out Vector3 worldPos) + { + worldPos = default; + + var skinnedMeshRenderers = GetComponentsInChildren(true); + if (skinnedMeshRenderers == null || skinnedMeshRenderers.Length == 0) + { + return false; + } + + Bounds combinedBounds = default; + bool hasAnySkinnedMesh = false; + for (int i = 0; i < skinnedMeshRenderers.Length; i++) + { + var renderer = skinnedMeshRenderers[i]; + if (renderer == null || renderer.sharedMesh == null) + { + continue; + } + + var meshBounds = renderer.sharedMesh.bounds; + var scale = renderer.transform.lossyScale; + var worldCenter = renderer.transform.TransformPoint(meshBounds.center); + var worldSize = new Vector3( + Mathf.Abs(meshBounds.size.x * scale.x), + Mathf.Abs(meshBounds.size.y * scale.y), + Mathf.Abs(meshBounds.size.z * scale.z) + ); + var currentBounds = new Bounds(worldCenter, worldSize); + if (debugNamePlateBounds) + { + Debug.Log($"[Cuong] [PlayerVisual] smr={renderer.name} localCenter={meshBounds.center} localSize={meshBounds.size} worldCenter={worldCenter} worldSize={worldSize} worldMaxY={currentBounds.max.y}"); + } + + if (!hasAnySkinnedMesh) + { + combinedBounds = currentBounds; + hasAnySkinnedMesh = true; + } + else + { + combinedBounds.Encapsulate(currentBounds); + } + } + + if (!hasAnySkinnedMesh) + { + if (debugNamePlateBounds) + { + Debug.LogWarning("[Cuong] [PlayerVisual] TryGetNamePlateAnchorWorld: no valid SkinnedMeshRenderer/sharedMesh."); + } + return false; + } + + // Use merged bounds center for X/Z and merged top for Y. + // 使用合并包围盒中心作为X/Z,使用合并包围盒顶部作为Y。 + worldPos = new Vector3(combinedBounds.center.x, combinedBounds.max.y, combinedBounds.center.z); + if (debugNamePlateBounds) + { + Debug.Log($"[Cuong] [PlayerVisual] combinedCenter={combinedBounds.center} combinedSize={combinedBounds.size} anchorWorld={worldPos}"); + } + return true; + } } } \ No newline at end of file