using UnityEngine; namespace HotUpdate { /// /// 用地表 MeshCollider 的碰撞高度作为放置 Y。原点在底面中点的物件直接用该值。 /// public static class GroundSit { const float RayStartY = 80f; const float RayLength = 160f; public static float Height(Vector3 xz, Transform searchRoot = null, float fallback = 0f) { Physics.SyncTransforms(); var cols = Collect(searchRoot); var offsets = new[] { Vector3.zero, new Vector3(0.35f, 0f, 0.35f), new Vector3(-0.35f, 0f, 0.35f), new Vector3(0.35f, 0f, -0.35f), }; foreach (var offset in offsets) { if (TryHit(cols, xz + offset, groundOnly: true, out var y)) return y; } foreach (var offset in offsets) { if (TryHit(cols, xz + offset, groundOnly: false, out var y)) return y; } return fallback; } static bool TryHit(MeshCollider[] cols, Vector3 xz, bool groundOnly, out float y) { var ray = new Ray(new Vector3(xz.x, RayStartY, xz.z), Vector3.down); var bestDist = float.MaxValue; y = 0f; var hitAny = false; foreach (var col in cols) { if (col == null || !col.enabled) continue; if (groundOnly && !IsGroundCollider(col)) continue; if (!col.Raycast(ray, out var hit, RayLength)) continue; if (hit.distance >= bestDist) continue; bestDist = hit.distance; y = hit.point.y; hitAny = true; } return hitAny; } static MeshCollider[] Collect(Transform searchRoot) { if (searchRoot != null) return searchRoot.GetComponentsInChildren(); return Object.FindObjectsByType(FindObjectsSortMode.None); } static bool IsGroundCollider(MeshCollider col) { for (var t = col.transform; t != null; t = t.parent) { if (t.name.IndexOf("ground", System.StringComparison.OrdinalIgnoreCase) >= 0) return true; } return false; } } }