Unity3d人物头顶坐标 取得在场景中头顶坐标

本文介绍了一种在Unity中获取人物模型尺寸的方法,包括头顶、中部和底部坐标。通过遍历模型的所有渲染器组件,计算出包围盒的最高点、中心点和最低点,适用于动画播放时的尺寸变化调整。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 红色球标识头顶,黄色中部,黑色底部,蓝色是包围盒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();
    }



}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值