当前位置: 首页 > news >正文

【Unity3D】摄像机适配场景以及Canvas适配

目录

宽度不变策略

高度不变策略


宽度不变策略

开发分辨率 750*1334     (宽高比:0.56)
真机分辨率 1170*2532   (宽高比:0.46)   
真机宽高比<开发宽高比,采用宽度不变策略

理由:小于代表真机高度比开发高度更大,因此不需要担心高度上的遮挡,而应担心宽度的遮挡,为了避免宽度上有遮挡问题,因此保证宽度不变策略;

左图是开发分辨率下的情况(宽度和高度均正常 设计上就是如此)

右图是真机分辨率下适配后的情况,保证宽度不变(可见宽度上的绿色边缘处是紧贴着屏幕边缘),但是高度绿色边缘没有紧贴白色的UI(问题1)(下面的一行白色是UGUI UGUI紧贴着屏幕底下边缘)

不启用适配状况:若不启用摄像机适配会呈现如下图:

宽度会被遮挡一部分!因此我们要保证宽度不变

问题1(高度绿色边缘没有紧贴白色的UI)解决思路:

将整个3D场景所有元素放于一个根节点下,往下移动一定距离来紧贴白色UI

移动的相对距离是  (适配后的摄像机size - 开发时摄像机size),移动后得到如下图

注意相对的是指摄像机空间下的相对距离(Y轴,即上下位移)

如下脚本挂载摄像机下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CameraAdapt : MonoBehaviour
{
    //开发宽度
    private int width = 750;
    //开发高度
    private int height = 1334;
    //摄像机原始size
    private float cameraSize;
    //场景物体根节点
    public Transform levelTrans;
    //CanvasScaler组件
    public CanvasScaler canvasScaler;
    //场景物体根节点世界位置
    Vector3 levelWorldPos;

    void Start()
    {
        //记录原始大小
        cameraSize = GetComponent<Camera>().orthographicSize;
        //场景世界位置
        levelWorldPos = levelTrans.position;

        AdaptCamera();
    }

    private void AdaptCamera()
    {
        Camera camera = Camera.main;

        float devRate = (750f / 1334f);
        float nowRate = (Screen.width * 1.0f / Screen.height);

        Debug.Log("开发宽高比:" + devRate);
        Debug.Log("真机宽高比:" + nowRate);

        //真机宽高比 > 开发宽高比时 , 例如  0.75 > 0.56  (目前是0.56) 说明真机更倾向高度不变策略  少于时更倾向宽度不变
        bool useHeight = false;
        if (nowRate > devRate)
        {
            useHeight = true; //你可尝试注释这一行来查看ipad之类的适配情况,如果依旧保持宽度不变 会发生某些场景物体可能会因高度变小而无法看到。
        }

        Debug.Log("Screen.width :" + Screen.width);
        Debug.Log("Screen.height :" + Screen.height);

        float changeRate = (width * 1.0f / height) * (Screen.height * 1.0f / Screen.width);
        Debug.Log(changeRate);

        //保证高度不变
        if (useHeight)
        {
            //原Unity策略就是高度不变,所以不需要任何算法,直接保持原样即可(这里cameraSize就是原始size)
            float targetHeight = cameraSize;
            camera.orthographicSize = targetHeight;

            //修改Canvas的适配为高度不变策略
            canvasScaler.matchWidthOrHeight = 1;
        }
        else
        {
            //保证宽度不变
            float targetWidth = cameraSize * (width * 1.0f / height);
            camera.orthographicSize = targetWidth * (Screen.height * 1.0f / Screen.width);
            //修改Canvas的适配为宽度不变策略
            canvasScaler.matchWidthOrHeight = 0;

            //在宽度不变情况下,拉长高度而导致场景空白区域变多后,将场景元素向下对齐的方案
            //即真机摄像机单位高度值 - 开发单位高度值 = 单位高度差值
            float deltaHeight = camera.orthographicSize - cameraSize;
            Debug.Log("camera.orthographicSize:" + camera.orthographicSize + ",cameraSize:" + cameraSize + ",deltaHeight:" + deltaHeight);

            //重置场景根节点世界坐标
            levelTrans.position = levelWorldPos;
            //延迟3秒后再偏移,目的是为了看到这个过程是如何变化让场景向下对齐
            StartCoroutine(DelayToSetPos(deltaHeight));
        }
    }

    IEnumerator DelayToSetPos(float deltaHeight)
    {
        yield return new WaitForSeconds(1f);
        //场景根节点世界坐标 转 位于摄像机下的局部坐标
        Vector3 levelLocalPos = this.transform.InverseTransformPoint(levelWorldPos);
        //进行高度偏移 (高度单位差) 即向下对齐
        levelLocalPos = new Vector3(levelLocalPos.x, levelLocalPos.y - deltaHeight, levelLocalPos.z);
        // 位于摄像机下的局部坐标 转 世界坐标  赋予场景根节点
        levelTrans.position = this.transform.TransformPoint(levelLocalPos);
    }

    // Update is called once per frame
    void Update()
    {
        //按下空格即可再次适配 (可以动态改Game 分辨率 测试不同分辨率下的情况)
        if (Input.GetKeyDown(KeyCode.Space))
        {
            AdaptCamera();
        }
    }
}

其他场景信息:

 

高度不变策略

当真机宽高比 > 开发宽高比时,采用高度不变策略

主要变化就是Canvas Scaler,摄像机size是保持不变的,Unity原本就是高度不变策略。

如果不采用高度不变策略会得到如下图,高度上会有场景元素被遮挡!

注释一行即可测试

 

相关文章:

  • 使用LangChain开发智能问答系统
  • c#难点整理
  • 【模板】计算几何入门
  • Vue 表单输入绑定,双向绑定
  • 基于 intellij-platform-plugin-template 开发IDEA插件注意事项
  • form 表单内容序列化成一个字符串
  • 大模型架构记录2 【综述-相关代码】
  • 数据服务赋能数据治理:从“One Service”到QuickAPI的演进
  • redis操作
  • 【搜索】dfs(回溯、剪枝、记忆化)
  • 【C++】类和对象(二)默认成员函数之构造函数、析构函数
  • Springboot集成Debezium监听postgresql变更
  • CQL学习
  • 游戏引擎学习第177天
  • 996引擎-接口测试:背包
  • pnpm 报错 Error: Cannot find matching keyid 解决
  • Mybatis的基础操作——03
  • 西交建筑学本科秋天毕业想转码,自学了Python+408,华为OD社招还是考研更香?
  • 第十四章:模板实例化_《C++ Templates》notes
  • 如何编写SLURM系统的GRES资源插件
  • 伊朗港口爆炸已致46人死亡
  • 经济日报:多平台告别“仅退款”,规则调整有何影响
  • 党旗下的青春|83岁仍在“下生活”,他说生活是创作的源泉
  • 铁路上海站五一假期预计发送446万人次,同比增长8.4%
  • 103岁抗战老兵、抗美援朝老兵、医学专家张道中逝世
  • 重新认识中国女性|婚姻,古代传统家庭再生产的根本之道