fix pos name text of matter

This commit is contained in:
CuongNV
2026-05-19 17:28:14 +07:00
parent 91ca5329ce
commit e2c24283cc
+116 -8
View File
@@ -37,6 +37,10 @@ namespace PerfectWorld.Scripts
public const uint MATTER_MONEY = 3;
public const uint MATTER_TYPEMASK = 0xff;
private const float MatterNameExtraWorldYOffset = 0.05f;
private const float MatterNameFallbackLocalY = 0.6f;
private const string ItemNameTextChildName = "ItemNameText";
// Constructor / Constructor
public CECMatter()
{
@@ -179,11 +183,24 @@ namespace PerfectWorld.Scripts
var collider = matterObject.AddComponent<BoxCollider>();
//this is a workaround to fix the collider size issue when load prefab go wrong at some point
//TODO: remove this workaround after the prefab load issue is fixed
Vector3 size = matterObject.GetComponentInChildren<Renderer>().bounds.size;
if (size.x < 0.5f) size.x = 0.5f;
if (size.y < 0.5f) size.y = 0.5f;
if (size.z < 0.5f) size.z = 0.5f;
collider.size = size;
if (TryGetCombinedRendererBounds(matterObject.transform, null, out var combinedBounds))
{
Vector3 size = combinedBounds.size;
if (size.x < 0.5f) size.x = 0.5f;
if (size.y < 0.5f) size.y = 0.5f;
if (size.z < 0.5f) size.z = 0.5f;
collider.size = size;
collider.center = matterObject.transform.InverseTransformPoint(combinedBounds.center);
}
else
{
var firstRenderer = matterObject.GetComponentInChildren<Renderer>();
Vector3 size = firstRenderer != null ? firstRenderer.bounds.size : Vector3.one;
if (size.x < 0.5f) size.x = 0.5f;
if (size.y < 0.5f) size.y = 0.5f;
if (size.z < 0.5f) size.z = 0.5f;
collider.size = size;
}
}
// Create text object to display item name above the cube
CreateItemNameText(matterObject, Info.tid);
@@ -244,18 +261,109 @@ namespace PerfectWorld.Scripts
return null;
}
/// <summary>
/// Merge world-space bounds of all child Renderers (MeshRenderer + SkinnedMeshRenderer).
/// Reads sharedMesh.bounds (mesh local space) and manually converts to world space —
/// same approach as PlayerVisual/NPCVisual — to avoid stale renderer.bounds after SetActive.
/// 合并所有子 Renderer 的世界包围盒(MeshRenderer + SkinnedMeshRenderer)。
/// 直接读 sharedMesh.bounds(网格本地空间)再手动转为世界坐标,避免 SetActive 后同帧 renderer.bounds 未刷新的问题。
/// </summary>
private static bool TryGetCombinedRendererBounds(Transform matterRoot, Transform excludeSubtree, out Bounds combinedBounds)
{
combinedBounds = default;
if (matterRoot == null)
return false;
var renderers = matterRoot.GetComponentsInChildren<Renderer>(true);
bool hasAny = false;
for (int i = 0; i < renderers.Length; i++)
{
var renderer = renderers[i];
if (renderer == null)
continue;
if (excludeSubtree != null && renderer.transform.IsChildOf(excludeSubtree))
continue;
Mesh mesh = null;
if (renderer is SkinnedMeshRenderer smr)
{
mesh = smr.sharedMesh;
}
else if (renderer is MeshRenderer)
{
var mf = renderer.GetComponent<MeshFilter>();
if (mf != null)
mesh = mf.sharedMesh;
}
if (mesh == null)
continue;
// Manually build world-space bounds from mesh-local bounds + transform,
// identical to PlayerVisual/NPCVisual — reliable even right after SetActive(true).
// 与 PlayerVisual/NPCVisual 相同:从网格本地包围盒手动计算世界包围盒,SetActive 后同帧可靠。
var meshBounds = mesh.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);
Debug.Log($"[Cuong] [CECMatter] renderer={renderer.name} meshLocalCenter={meshBounds.center} meshLocalSize={meshBounds.size} worldCenter={worldCenter} worldSize={worldSize} worldMaxY={currentBounds.max.y}");
if (!hasAny)
{
combinedBounds = currentBounds;
hasAny = true;
}
else
{
combinedBounds.Encapsulate(currentBounds);
}
}
return hasAny;
}
/// <summary>
/// Name anchor at top of tallest mesh: combinedBounds.max.y in world, converted to local.
/// 名牌锚点位于最高 mesh 顶部:世界坐标 combinedBounds.max.y,再转为本地坐标。
/// </summary>
private static bool TryGetItemNameAnchorLocal(Transform matterRoot, out Vector3 localAnchor)
{
if (!TryGetCombinedRendererBounds(matterRoot, null, out var combinedBounds))
{
localAnchor = new Vector3(0f, MatterNameFallbackLocalY, 0f);
return false;
}
var worldAnchor = new Vector3(
combinedBounds.center.x,
combinedBounds.max.y + MatterNameExtraWorldYOffset,
combinedBounds.center.z);
localAnchor = matterRoot.InverseTransformPoint(worldAnchor);
return true;
}
private static void CreateItemNameText(GameObject matterObject, int tid)
{
if (matterObject == null)
return;
// Avoid duplicating if prefab already contains it (or Init called twice).
if (matterObject.transform.Find("ItemNameText") != null)
if (matterObject.transform.Find(ItemNameTextChildName) != null)
return;
var textObject = new GameObject("ItemNameText");
var textObject = new GameObject(ItemNameTextChildName);
textObject.transform.SetParent(matterObject.transform, false);
textObject.transform.localPosition = new Vector3(0f, 0.6f, 0f);
if (!TryGetItemNameAnchorLocal(matterObject.transform, out var localAnchor))
{
Debug.LogWarning(
$"[Cuong] [CECMatter] No renderer bounds for '{matterObject.name}'; using fallback Y={MatterNameFallbackLocalY}");
}
textObject.transform.localPosition = localAnchor;
var textMesh = textObject.AddComponent<TextMeshPro>();