unity实现使不同尺寸物体总能较完整的显示在相机范围内

unity通常会有将一个模型展示在界面上用于旋转缩放观察的功能,通常是使用相机看向模型,然后使用RawImage显示相机看到的画面。

但很多时候展示的模型尺寸与中心点是差别较大的,就需要自适应的修改模型的位置使其能够完整的显示在屏幕中心位置,下面就是相关代码

首先将物体的子物体进行网格合并

cs 复制代码
/// <summary>
/// 计算模型全部展示位置
/// </summary>
/// <param name="root"></param>
/// <returns></returns>
private Vector3 CalculateAllShowPos(GameObject root)
{
    if (root.transform.childCount > 0)
    {
        //若物体是由一堆子物体拼接而成, 可以先将他们的Mesh合并
        MeshFilter[] meshFilters = root.transform.GetComponentsInChildren<MeshFilter>();
        CombineInstance[] combineInstances = new CombineInstance[meshFilters.Length];
        for (int k = 0; k < meshFilters.Length; k++)
        {
            combineInstances[k].mesh = meshFilters[k].sharedMesh;
            combineInstances[k].transform = meshFilters[k].transform.localToWorldMatrix;
        }
        Mesh newMesh = new Mesh();
        //若合并的Mesh面数太多,需要将indexFormat 改为UInt32,默认为UInt16
        newMesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
        newMesh.CombineMeshes(combineInstances);
        root.AddComponent<MeshFilter>().sharedMesh = newMesh;

        float xx = root.GetComponent<MeshFilter>().sharedMesh.bounds.size.x;
        float yy = root.GetComponent<MeshFilter>().sharedMesh.bounds.size.y;
        float zz = root.GetComponent<MeshFilter>().sharedMesh.bounds.size.z;

        float sizeF = 1;

        if (xx >= yy)
        {
            sizeF = xx;
        }
        else
        {
            sizeF = yy;
        }

        if (sizeF >= zz)
        {

        }
        else
        {
            sizeF = zz;
        }
        //返回模型需要向后移动的距离
        return new Vector3(0, 0, 1.1f * sizeF);
    }

    return Vector3.zero;
}

然后计算物体的几何中心点

cs 复制代码
/// <summary>
/// 计算模型的几何中心点(局部坐标系下)
/// </summary>
/// <param name="tran"></param>
/// <returns></returns>
private Vector3 CalculateModelCenter(Transform tran)
{
    //计算每个子物体包围盒的中心,平均值得到物体的中心
    Vector3 center = Vector3.zero;
    Renderer[] renders = tran.GetComponentsInChildren<Renderer>();
    foreach (Renderer child in renders)
    {
        center += child.bounds.center;
    }
    center /= renders.Length;
    //返回相对位置
    return center - tran.position;
}

最后就是展示模型时调用上述方法即可

cs 复制代码
/// <summary>
/// 显示三维模型
/// </summary>
public void ShowModel(GameObject model)
{
    if (model == null)
    {
        return;
    }
    
    //ModelPoint为相机看向的点,是一个空物体,将展示的物体设置为其子物体
    model.transform.SetParent(ModelPoint, false);

    Vector3 backPos = CalculateAllShowPos(model);
    Vector3 pos = CalculateModelCenter(model.transform);

    model.transform.localPosition = -pos;
    model.transform.localRotation = Quaternion.identity;
    ModelPoint.position += backPos;
}

需要注意的是创建RenderTexture时需要设置宽高与展示模型的RawImage的宽高一致,否则展示画面会有拉伸感

cs 复制代码
 private void Start()
 {
     RenderTexture render = new RenderTexture((int)rect.rect.width, (int)rect.rect.height, 16, RenderTextureFormat.ARGB32);
    ModelCamera.targetTexture = render;
    ModelImage.texture = render;
 }

最后是简单的鼠标控制相机拖拽,旋转,缩放观察模型,若需要射线监测点击物体则需要注意射线发射的位置为鼠标位置减去RawImage左下角位置,具体代码如下

cs 复制代码
using BehaviorDesigner.Runtime.Tasks.Unity.UnityPhysics;
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem.HID;

/// <summary>
/// 控制模型移动,旋转,缩放,点击模型
/// </summary>
public class ModelControl : MonoBehaviour,IPointerEnterHandler,IPointerExitHandler
{
    /// <summary>
    /// 模型相机
    /// </summary>
    public Camera ModelCamera;
    /// <summary>
    /// 模型父节点
    /// </summary>
    public Transform ModelPoint;

    public RectTransform ModelRect;

    /// <summary>
    /// 是否鼠标进入操作区域
    /// </summary>
    private bool isMouseEnter = false;

    RaycastHit hit;

    public void OnPointerEnter(PointerEventData eventData)
    {
        isMouseEnter = true;
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        isMouseEnter = false;
    }

    private void Update()
    {
        if (!isMouseEnter)
            return;

        if(Input.GetMouseButton(0))
        {
            float x = Input.GetAxis("Mouse X");
            float y = Input.GetAxis("Mouse Y");

            //相机上下左右移动实现拖拽移动
            ModelCamera.transform.position -= ModelCamera.transform.right * x * 0.2f;
            ModelCamera.transform.position -= ModelCamera.transform.up * y * 0.2f;
        }

        float scrollWheel = Input.GetAxis("Mouse ScrollWheel");

        if(scrollWheel != 0)
        {
            //相机前后移动实现缩放
            ModelCamera.transform.position += ModelCamera.transform.forward * scrollWheel * 10f;
        }

        if(Input.GetMouseButton(2))
        {
            float x = Input.GetAxis("Mouse X");
            float y = Input.GetAxis("Mouse Y");

            float xnew = Mathf.Abs(x);
            float ynew = Mathf.Abs(y);

            if(xnew > ynew)
            {
                ModelCamera.transform.RotateAround(ModelPoint.position, Vector3.up, x * 5f);
            }
            else
                ModelCamera.transform.RotateAround(ModelPoint.position, ModelCamera.transform.right, -y * 5f);
        }
        
        //右键点击
        if(Input.GetMouseButtonDown(1))
        {
            //为保证射线在任何分辨率下无异常,需要进行位置的换算,其中1920与1080为CanvasScaler设定的值,若不为1920*1080需要对应修改
            float x = 1920f / Screen.width;
            float y = 1080f / Screen.height;

            //鼠标位置减去模型Image左下角位置
            Vector3 mousePos = Input.mousePosition;
            mousePos = new Vector3(mousePos.x * x, mousePos.y * y);

            Vector3 rectPos = ModelRect.position;
            rectPos = new Vector3(rectPos.x * x, rectPos.y * y);

            Vector3 leftDownPos = rectPos - new Vector3(ModelRect.rect.width * 0.5f, ModelRect.rect.height * 0.5f);

            Ray ray = ModelCamera.ScreenPointToRay(mousePos - leftDownPos);

            if (Physics.Raycast(ray, out hit, 300)) //如果碰撞检测到物体
            {
                //点击到物体,自行处理
            }
        }
    }
}
相关推荐
FLJwu40 分钟前
镜头模组与画质量产实战 02:运动相机镜头组装工艺、胶水选型、温变跑焦与参数虚标|20 年源头工厂量产经验
经验分享·数码相机·产品运营·相机
玖玥拾1 小时前
Lua 基础语法(六)xLua Lua 调用 C# 交互
开发语言·unity·c#·lua
xcLeigh15 小时前
Unity基础:MonoBehaviour的生命周期——Awake、OnEnable、Start、Update执行顺序
unity·游戏引擎
qq_2131578916 小时前
其域lcc2丢失部分lod/远处显示空洞的解决方式
unity·lcc2
辻弋20117 小时前
自动检测硬件生成驱动清单、实时监控FPS、120秒性能记录——30+项优化模块化开关,所有改动可精确复原
服务器·windows·游戏引擎·电脑·开源软件
点心的游戏开发世界19 小时前
GDScript 入门笔记:目录
开发语言·笔记·游戏引擎·godot
XR技术研习社19 小时前
关于 PICO 串流测试中报错 IndexOutOfRangeException: renderPassIndex 的两个排障方法
unity·ar·xr·vr
想做后端的前端19 小时前
Unity · 性能优化:内存管理完全指南:从托管堆到跨桥开销
unity·性能优化·游戏引擎
GlobalInfo1 天前
2026-2032年全球固定相机式工业机器人视觉系统行业产量、产值及主要企业格局分析报告
数码相机·机器人
平行云1 天前
实时云渲染信创架构解析:从GPU池化到全栈适配的技术演进
linux·unity·docker·ue5·webgl·数字孪生·实时云渲染