红色球标识头顶,黄色中部,黑色底部,蓝色是包围盒8个点,红线是包围盒范围
第二版
实现步奏
获取 物体下的所有Renderer,然后通过Renderer.bounds 外扩包围盒 找到 最高上\ 中 \最低的下 这3个点
缺点 播放动画时 外扩包围盒 随着动画 缩放 top点会上下移动 , 比如抬手动画
注意:
Renderer 包含了所有 SkinnedMeshRenderer 和 MeshFilter
Renderer .bounds 外扩包围盒 已经是世界坐标了,using System; using System.Linq; using UnityEngine; public struct meshBoundsPoint { public meshBoundsPoint(Vector3 top, Vector3 center, Vector3 bottom) { this.top = top; this.center = center; this.bottom = bottom; } public meshBoundsPoint(Bounds bounds) { center = bounds.center; var hight_y = bounds.size.y; var topPoint = center; topPoint.y += hight_y * 0.5f; var bottomPoint = center; bottomPoint.y -= hight_y * 0.5f; top = topPoint; bottom = bottomPoint; } public Vector3 top; public Vector3 center; public Vector3 bottom; } public enum meshBoundsType { top = 0, center, bottom, } public class ModelSizeGet : MonoBehaviour { public static meshBoundsPoint GetSize(Transform transform) { Renderer[] renders = transform.GetComponentsInChildren<Renderer>(); var arr = new meshBoundsPoint[renders.Length]; int i = -1; foreach (var render in renders) { arr[++i] = new meshBoundsPoint(render.bounds); // DrawWorldBoundLine(bounds, Color.white, 0.1f); } var order = arr.OrderByDescending(x => x.top.y); var maxY = order.First().top; var order2 = arr.OrderByDescending(x => x.bottom.y); var minY = order2.Last().bottom; float hight = maxY.y - minY.y; var centerPos = minY; centerPos.y = minY.y + hight * 0.5f; maxY.x = minY.x; maxY.z = minY.z; return new meshBoundsPoint(maxY,centerPos,minY); } public static Vector3 GetSize(Transform transform,meshBoundsType mp) { var pos = GetSize(transform); switch (mp) { case meshBoundsType.top: return pos.top; break; case meshBoundsType.center: return pos.center; break; case meshBoundsType.bottom: return pos.bottom; break; default: throw new ArgumentOutOfRangeException("mp", mp, null); } } public static void DrawLocalBoundLine(Bounds bounds, Transform sourceTran, Color color = default(Color), float duration = float.MaxValue) { Vector3[] points = new Vector3[8]; var width_x = bounds.size.x * sourceTran.lossyScale.x; var hight_y = bounds.size.y * sourceTran.lossyScale.y; var length_z = bounds.size.z * sourceTran.lossyScale.z; var LeftBottomPoint = sourceTran.TransformPoint(bounds.min); var rightUpPoint = sourceTran.TransformPoint(bounds.max); var centerPoint = sourceTran.TransformPoint(bounds.center); var topPoint = new Vector3(centerPoint.x, centerPoint.y + hight_y / 2, centerPoint.z); var bottomPoint = new Vector3(centerPoint.x, centerPoint.y - hight_y / 2, centerPoint.z); points[0] = LeftBottomPoint + Vector3.right * width_x; points[1] = LeftBottomPoint + Vector3.up * hight_y; points[2] = LeftBottomPoint + Vector3.forward * length_z; points[3] = rightUpPoint - Vector3.right * width_x; points[4] = rightUpPoint - Vector3.up * hight_y; points[5] = rightUpPoint - Vector3.forward * length_z; points[6] = LeftBottomPoint; points[7] = rightUpPoint; Debug.DrawLine(LeftBottomPoint, points[0], color, duration); Debug.DrawLine(LeftBottomPoint, points[1], color, duration); Debug.DrawLine(LeftBottomPoint, points[2], color, duration); Debug.DrawLine(rightUpPoint, points[3], color, duration); Debug.DrawLine(rightUpPoint, points[4], color, duration); Debug.DrawLine(rightUpPoint, points[5], color, duration); Debug.DrawLine(points[1], points[3], color, duration); Debug.DrawLine(points[2], points[4], color, duration); Debug.DrawLine(points[0], points[5], color, duration); Debug.DrawLine(points[2], points[3], color, duration); Debug.DrawLine(points[0], points[4], color, duration); Debug.DrawLine(points[1], points[5], color, duration); //Debug.DrawLine(topPoint, centerPoint, color, duration); //Debug.DrawLine(bottomPoint, centerPoint, color, duration); //foreach (var item in points) //{ // DrawPoint(item, color, duration); //} } public static void DrawWorldBoundLine(Bounds bounds, Color color = default(Color), float duration = float.MaxValue) { Vector3[] points = new Vector3[8]; var width_x = bounds.size.x ; var hight_y = bounds.size.y ; var length_z = bounds.size.z ; var LeftBottomPoint = bounds.min; var rightUpPoint = bounds.max; var centerPoint = bounds.center; var topPoint = new Vector3(centerPoint.x, centerPoint.y + hight_y / 2, centerPoint.z); var bottomPoint = new Vector3(centerPoint.x, centerPoint.y - hight_y * 0.5f, centerPoint.z); points[0] = LeftBottomPoint + Vector3.right * width_x; points[1] = LeftBottomPoint + Vector3.up * hight_y; points[2] = LeftBottomPoint + Vector3.forward * length_z; points[3] = rightUpPoint - Vector3.right * width_x; points[4] = rightUpPoint - Vector3.up * hight_y; points[5] = rightUpPoint - Vector3.forward * length_z; points[6] = LeftBottomPoint; points[7] = rightUpPoint; Debug.DrawLine(LeftBottomPoint, points[0], color, duration); Debug.DrawLine(LeftBottomPoint, points[1], color, duration); Debug.DrawLine(LeftBottomPoint, points[2], color, duration); Debug.DrawLine(rightUpPoint, points[3], color, duration); Debug.DrawLine(rightUpPoint, points[4], color, duration); Debug.DrawLine(rightUpPoint, points[5], color, duration); Debug.DrawLine(points[1], points[3], color, duration); Debug.DrawLine(points[2], points[4], color, duration); Debug.DrawLine(points[0], points[5], color, duration); Debug.DrawLine(points[2], points[3], color, duration); Debug.DrawLine(points[0], points[4], color, duration); Debug.DrawLine(points[1], points[5], color, duration); //Debug.DrawLine(topPoint, centerPoint, color, duration); //Debug.DrawLine(bottomPoint, centerPoint, color, duration); //foreach (var item in points) //{ // DrawPoint(item, color, duration); //} } public static Transform DrawPoint(Vector3 pos, Color color = default(Color), float duration = float.MaxValue) { var scale = 0.08f; var temp = GameObject.CreatePrimitive(PrimitiveType.Sphere); temp.transform.position = pos; temp.transform.localScale = new Vector3(scale, scale, scale); temp.GetComponent<Renderer>().material.color = color; var light = temp.AddComponent<Light>(); light.type = LightType.Point; light.color = color; GameObject.Destroy(temp.GetComponent<Collider>()); GameObject.Destroy(temp, duration); return temp.transform; } void OnDrawGizmos() { DrawGizmos(new Color(0, 0, 0, 0.2f)); } void OnDrawGizmosSelected() { DrawGizmos(new Color(0, 0, 1, .2f)); } void DrawGizmos(Color col) { Gizmos.color = col; Renderer[] renders = transform.GetComponentsInChildren<Renderer>(); foreach (var render in renders) { Gizmos.DrawCube(render.bounds.center, render.bounds.size); } } void Update() { var pos = GetSize(transform); DrawPoint(pos.top, Color.red, 0.1f); DrawPoint(pos.center, Color.green, 0.1f); DrawPoint(pos.bottom, Color.blue, 0.1f); } }
第一版
实现步奏
取该物体上的所有SkinnedMeshRenderer组件,然后从取得模型数组中找出顶点最多的模型,就是人物身体模型,因为人物身体模型顶点面片肯定是最多,然后通过模型取得包围盒,然后就把包围盒从模型坐标转成世界坐标.
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; /// <summary> /// 通过包围盒取得人物在场景中头顶中间底部坐标 /// </summary> public class ESize : MonoBehaviour { /// <summary> /// 是否开启辅助画线画点 /// </summary> public bool isDrawPointLine = false; public float width_x; public float hight_y; public float length_z; public Vector3 topPoint = Vector3.zero; public Vector3 centerPoint = Vector3.zero; public Vector3 bottomPoint = Vector3.zero; public Vector3 LeftBottomPoint = Vector3.zero; public Vector3 rightUpPoint = Vector3.zero; public Transform topTran; public Transform bottomTran; public Transform centerTran; public Transform rootBone; public Bounds bounds = new Bounds(); public Transform boundsTransform; public Transform cubeTransform; Vector3[] points = new Vector3[8]; /// <summary> /// 找到身体的 bounds ///因为模型中有好多部件 比如眼睛 武器等 但是我们只要人物身体都模型,因为人物身体模型顶点面片肯定是最多, 比武器这些多 /// </summary> /// <returns></returns> void FindBodyBounds() { Object[] arr; arr = gameObject.GetComponentsInChildren<SkinnedMeshRenderer>();//人物角色模型用SkinnedMeshRenderer 而普通模型用 MeshFilter if (arr == null || arr.Length == 0) { arr = gameObject.GetComponentsInChildren<MeshFilter>(); } if (arr == null || arr.Length == 0) { Debug.LogError(gameObject.name = ", no Find Bounds"); } if (arr != null) { int maxCount = 0; foreach (var item in arr) { if (item is SkinnedMeshRenderer) { var i = (SkinnedMeshRenderer)item; if (i.sharedMesh.vertices.Length > maxCount) { maxCount = i.sharedMesh.vertices.Length; bounds = i.sharedMesh.bounds; rootBone = i.rootBone; boundsTransform = i.gameObject.transform; // Debug.Log("物体名称:" + i.gameObject.name + ",顶点坐标数量 :" + i.sharedMesh.vertices.Length + ",三角形序列数量 :" + i.sharedMesh.triangles.Length + ",纹理数量 :" + i.sharedMesh.uv.Length + ",法线数量 :" + i.sharedMesh.normals.Length); } } else if (item is MeshFilter) { var i = (MeshFilter)item; if (i != null && i.mesh != null) { if (i.mesh.vertices.Length > maxCount) { maxCount = i.mesh.vertices.Length; bounds = i.mesh.bounds; boundsTransform = i.gameObject.transform; // Debug.Log("物体名称:" + i.gameObject.name + ",顶点坐标数量 :" + i.sharedMesh.vertices.Length + ",三角形序列数量 :" + i.sharedMesh.triangles.Length + ",纹理数量 :" + i.sharedMesh.uv.Length + ",法线数量 :" + i.sharedMesh.normals.Length); } } else { Debug.LogError(i.gameObject.name = ", no Find MeshFilter"); } } } } if(bounds==null || boundsTransform == null) { Debug.LogError(gameObject.name = ", no Find MeshFilter"); } } void CalculationPoint() { LeftBottomPoint = transform.TransformPoint(bounds.min); rightUpPoint = transform.TransformPoint(bounds.max); centerPoint = transform.TransformPoint(bounds.center); width_x = bounds.size.x * boundsTransform.lossyScale.x; hight_y = bounds.size.y * boundsTransform.lossyScale.y; length_z = bounds.size.z * boundsTransform.lossyScale.z; topPoint = centerPoint; topPoint.y += hight_y/2; bottomPoint = centerPoint; bottomPoint.y -= hight_y / 2; if(Terrain.activeTerrain) { var Terrain_hight = Terrain.activeTerrain.SampleHeight(bottomPoint); bottomPoint.y = Terrain_hight + 0.2f; } // Debug.Log("物体名称:" + boundsTransform.name + //",长 :" + length_z + //",宽:" + +width_x + //",高:" + hight_y + // ",topPoint" + topPoint + // ",centerPoint" + centerPoint + // ",bottomPoint" + bottomPoint //); points[0] = LeftBottomPoint + Vector3.right * width_x; points[1] = LeftBottomPoint + Vector3.up * hight_y; points[2] = LeftBottomPoint + Vector3.forward * length_z; points[3] = rightUpPoint - Vector3.right * width_x; points[4] = rightUpPoint - Vector3.up * hight_y; points[5] = rightUpPoint - Vector3.forward * length_z; points[6] = LeftBottomPoint; points[7] = rightUpPoint; //如果轴向不对的话,这时候就需要旋转,不规范模型,懒得不计算包围盒的8个点,只计算上中下了----------------- if (true) { if(rootBone==null) { var hight = 1.65f; var nav = gameObject.GetComponent<NavMeshAgent>(); if (nav) { hight = nav.height; } if (Terrain.activeTerrain) { var Terrain_hight = Terrain.activeTerrain.SampleHeight(boundsTransform.position); bottomPoint.y = Terrain_hight + 0.2f; topPoint.y += hight; centerPoint.y += hight / 2; } else { bottomPoint = boundsTransform.position; topPoint = centerPoint; topPoint.y += hight; centerPoint.y += hight / 2; } } else { TryGet(); } } } void TryGet() {//如果轴向不对的话,这时候就需要旋转,不规范模型,懒得不计算包围盒的8个点,只计算上中下了----------------- var temp = GameObject.CreatePrimitive(PrimitiveType.Cube); cubeTransform = temp.transform; temp.transform.position = centerPoint; temp.transform.localScale = new Vector3(width_x, hight_y, length_z); temp.GetComponent<Renderer>().material.color = new Color(0, 1, 0, 1f / 255f); Destroy(temp.GetComponent<Collider>()); Destroy(temp.GetComponent<MeshRenderer>()); if (boundsTransform.localEulerAngles != Vector3.zero) { cubeTransform.localEulerAngles = boundsTransform.localEulerAngles; var upDirect = cubeTransform.up.normalized; if (rootBone != null) { centerPoint = cubeTransform.position = rootBone.position; var y = hight_y; var x = width_x; var z = length_z; if (upDirect.y == 1.0f)//y轴正直向上。 { return; } else if (upDirect.x == -1f)//=-1,x轴垂直的朝上,=1,x轴垂直的朝下 { y = width_x; x = hight_y; } else if (upDirect.z == -1f)//z轴朝上 { y = length_z; z = hight_y; } topPoint = centerPoint; topPoint.y += y / 2; bottomPoint = centerPoint; bottomPoint.y -= y / 2; temp.name = boundsTransform.name + ",x :" + x + ",y:" + +y + ",z:" + z; points[6] = bottomPoint; points[7] = topPoint; //Debug.LogError("cube.up.normalized" + cubeTransform.up.normalized); } } } void DrawBoundsPoint() { GameObject temp = null; float scale = 0.08f; //temp = GameObject.CreatePrimitive(PrimitiveType.Cube); //temp.transform.position = centerPoint; //temp.transform.localScale = new Vector3(width_x, hight_y, length_z); //temp.GetComponent<Renderer>().material.color = new Color(0, 1, 0, 1f / 255f); //Destroy(temp.GetComponent<Collider>()); //temp.name = boundsTransform.name + ",x :" + width_x + ",y:" + hight_y + ",z:" + length_z ; //Destroy(temp.GetComponent<MeshRenderer>()); temp = GameObject.CreatePrimitive(PrimitiveType.Sphere); temp.transform.parent = transform; temp.transform.localScale = new Vector3(scale, scale, scale); temp.transform.position = topPoint; temp.GetComponent<Renderer>().material.color = Color.red; //当材质球的Shader为标准时,可直接使用此方法修改颜色值 Destroy(temp.GetComponent<Collider>()); if (!isDrawPointLine) Destroy(temp.GetComponent<Renderer>()); topTran = temp.transform; topTran.name = "topTran"; temp = GameObject.CreatePrimitive(PrimitiveType.Sphere); temp.transform.parent = transform; temp.transform.position = centerPoint; temp.transform.localScale = new Vector3(scale, scale, scale); temp.GetComponent<Renderer>().material.color = Color.yellow; //当材质球的Shader为标准时,可直接使用此方法修改颜色值 Destroy(temp.GetComponent<Collider>()); if (!isDrawPointLine) Destroy(temp.GetComponent<Renderer>()); centerTran = temp.transform; centerTran.name = "centerTran"; temp = GameObject.CreatePrimitive(PrimitiveType.Sphere); temp.transform.parent = transform; temp.transform.position = bottomPoint; temp.transform.localScale = new Vector3(scale, scale, scale); temp.GetComponent<Renderer>().material.color = Color.black; //当材质球的Shader为标准时,可直接使用此方法修改颜色值 Destroy(temp.GetComponent<Collider>()); if (!isDrawPointLine) Destroy(temp.GetComponent<Renderer>()); bottomTran = temp.transform; bottomTran.name = "bottomTran"; if (isDrawPointLine) for (int i = 0; i < points.Length; i++) { temp = GameObject.CreatePrimitive(PrimitiveType.Sphere); temp.name = boundsTransform.name + "]points[" + i + "]"; temp.transform.position = points[i]; temp.transform.localScale = new Vector3(scale, scale, scale); temp.GetComponent<Renderer>().material.color = Color.blue; //当材质球的Shader为标准时,可直接使用此方法修改颜色值 Destroy(temp.GetComponent<Collider>()); } } void DrawBoundsLine() { Color color = Color.red; Debug.DrawLine(LeftBottomPoint, points[0], color); Debug.DrawLine(LeftBottomPoint, points[1], color); Debug.DrawLine(LeftBottomPoint, points[2], color); Debug.DrawLine(rightUpPoint, points[3], color); Debug.DrawLine(rightUpPoint, points[4], color); Debug.DrawLine(rightUpPoint, points[5], color); Debug.DrawLine(points[1], points[3], color); Debug.DrawLine(points[2], points[4], color); Debug.DrawLine(points[0], points[5], color); Debug.DrawLine(points[2], points[3], color); Debug.DrawLine(points[0], points[4], color); Debug.DrawLine(points[1], points[5], color); Debug.DrawLine(topPoint, centerPoint, color); Debug.DrawLine(bottomPoint, centerPoint, color); } bool isInit = false; public ESize Init() { if(!isInit) { FindBodyBounds(); CalculationPoint(); DrawBoundsPoint(); isInit = true; } return this; } void Start() { Init(); } // Update is called once per frame void Update() { if (isDrawPointLine) DrawBoundsLine(); } }