Unity开发笔记6

🔴离散网格地图制作(类似明日方舟基建)

前置:屏幕适配

我们在UI里面可以很轻松的完成屏幕适配。可是在2d场景中,并没有类似锚点的功能。那么我们来讲解一下到底要怎么完成屏幕适配

一、正交相机(Orthographic Camera)

正交相机是什么?

✅ 去掉透视效果的摄像机

✅ 所见即世界坐标单位

✅ 物体远近不影响大小(Orthographic 下没有"近大远小")

3个核心知识点(重点)

知识点 1:Size 决定"世界高度"

csharp 复制代码
Camera.orthographicSize = 5;

垂直方向能看到 10 个单位的世界

↑ 5

↓ 5

调大一点

调小一点

知识点 2:宽度是"算出来的"

可见世界高度 = orthographicSize × 2

可见世界宽度 = 可见世界高度 × aspect

aspect代表屏幕宽高比

csharp 复制代码
float worldWidth = Camera.main.orthographicSize * 2f * Camera.main.aspect;


aspect是不可以随便改的哦!就是这个东西:

16:9的图像宽高比,可以看到上下有很宽的黑边

自由宽高比

知识点 3:Unity 单位 ≠ 像素

Pixels Per Unit = 100

✅ Sprite 大小只和 PPU 有关

✅ 和分辨率无关

综上,我们可以看出,如果你的游戏是以16:9的比例开发的,但是如果用户用了一个1:1的屏幕打开,那么左右两边的画面必然有大量缺失!

二、关于屏幕适配最大的困扰

如果我的游戏是基于16:9的长宽比开发的,那用户用1:1的屏幕打开不就炸了吗?

是的------如果你"一切布局都死死按 16:9 摆",一旦变成 1:1,画面一定会"毁"

但关键在于:

❗ "毁"是正常的,也是可控的​

❗ 商业游戏不是"避免毁",而是决定怎么毁

为什么商业游戏还能"看起来没问题"?

因为它们从一开始就接受了这个事实,并做了三件事:

✅ 第一件事:选一个"主比例",其它比例都算异常

主比例 = 完美体验

其它比例 = 允许牺牲一部分体验

✅ 第二件事:决定"牺牲哪一边"

✅ 第三件事:布局时根本不把东西贴边

综上,游戏画面(2D 场景 / Sprite)的适配,不像 UI 那样让元素随屏幕动态缩放锚定;
而是先决定一个"设计画面宽高",再通过调整相机或留余量,决定牺牲哪一部分画面来适配不同屏幕。

🔴摄像机控制画面的偏移和缩放

画面偏移我就不说了,就是摄像机位置的移动。重点讲一下缩放的原理,我们刚才讲了orthographicSize。

orthographicSize决定的是:摄像机在"垂直方向"能看到多少个世界单位(Unity Unit)

它不是缩放系数

✅ 它不是分辨率

✅ 它是世界空间的裁剪高度

为什么改 size 看起来像"画面缩放"?

世界里的东西大小没变

相机"看多高"变了

所以画面看起来变大或变小

这是视觉缩放,不是几何缩放

为什么 size 不能直接叫"Zoom"?

Unity 故意不给它起名叫 zoom(缩放),是为了提醒你:

⚠️ 它不是"放大物体",而是"放大你看到的世界"

PPU (Pixels Per Unit)

PPU是什么?

PPU 代表每世界单位包含多少个纹理像素(Texture Pixels)

Texture n. 质地;口感;手感;

Texture Pixels n.纹理像素

如何修改PPU

PPU(Pixels Per Unit)的修改位置在图片资源的导入设置(Import Settings)里

在 Project 项目面板中,选中你要修改的 Sprite 图片(注意是图片资源本身,不是场景里的 Sprite 对象)


PPU 越大 → Sprite 在世界里越小
PPU 越小 → Sprite 在世界里越大

这个原理非常简单,但极其重要。

假设你有一张贴图:

图片尺寸:200 × 200 像素

✅ PPU = 100

200 ÷ 100 = 2

👉 这张 Sprite 在世界里占 2 × 2 个单位

✅ PPU = 200

200 ÷ 200 = 1

👉 这张 Sprite 在世界里占 1 × 1 个单位

✅ PPU = 50

200 ÷ 50 = 4

👉 这张 Sprite 在世界里占 4 × 4 个单位

修改图片缩放和修改PPU的区别

简单实现

Hierarchy中添加以下对象,Rooms,Room_A,CameraaController都创建空对象。

在Room_A中添加SpriteRender和Polygon Colider(不规则碰撞体)

创建以下脚本

Room.cs

csharp 复制代码
using UnityEngine;

public class Room : MonoBehaviour
{
    [Header("Camera Settings")]
    public float cameraSize = 5f; //聚焦后的正交摄像机size

    /// <summary>
    /// 动态获取room的位置以确定摄像机要对准的位置
    /// </summary>
    public Vector3 CameraTargetPos =>
        new Vector3(
            transform.position.x,
            transform.position.y,
            -10f
        );
}

RoomClick.cs

csharp 复制代码
using UnityEngine;

public class RoomClick : MonoBehaviour
{
    private Room room;

    void Awake()
    {
        room = GetComponent<Room>();
    }

    void OnMouseDown() //OnMouseDown()是 Unity 内置的生命周期函数
    {
        Debug.Log($"[RoomClick] Clicked = {gameObject.name}, pos={transform.position}");
        CameraController.Instance.FocusRoom(room);
    }
}

CameraController.cs

csharp 复制代码
using UnityEngine;

public class CameraController : MonoBehaviour
{
    public static CameraController Instance;

    [Header("Camera")]
    public Camera cam;

    [Header("Movement")]
    public float moveSpeed = 5f;
    public float zoomSpeed = 5f;

    private Vector3 targetPos;
    private float targetSize;

    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;

        if (cam == null)
            cam = Camera.main;
    }

    void Start()
    {
        targetPos = cam.transform.position;
        targetSize = cam.orthographicSize;
    }

    void Update()
    {
    	//Vector3.Lerp和 Mathf.Lerp的思想完全一样,只是作用对象从「一个 float」变成了「一个三维向量」。
        cam.transform.position = Vector3.Lerp(
            cam.transform.position,
            targetPos,
            Time.deltaTime * moveSpeed
        );

        //Lerp = Linear Interpolation(线性插值)
        //Lerp的作用是在两个值之间,按一个比例取中间值,因为orthographicSize一只在变,会一直触发Update,直到targetSize与orthographicSize相等
        cam.orthographicSize = Mathf.Lerp(
            cam.orthographicSize, //起始值
            targetSize, //目标值
            Time.deltaTime * zoomSpeed //插值比例(0~1)
        );
    }

    /// <summary>
    /// 聚焦单个房间
    /// </summary>
    public void FocusRoom(Room room)
    {
        if (room == null) return;

        targetPos = room.CameraTargetPos;
        targetSize = room.cameraSize;
    }

    /// <summary>
    /// 回到全局视图
    /// </summary>
    public void FocusGlobal()
    {
        targetPos = new Vector3(0f, 0f, -10f);
        targetSize = 10f;
    }
}

Room.cs和RoomClick.cs都挂载在Room_A,这两个代码最好不要合并(解耦),CameraController 挂载在CameraController 上面。在UI中添加一个退回全屏按钮,把FocusGlobal挂载上去。

效果:

点击任意一个room缩放

点击button退回全局

相关推荐
游乐码1 小时前
Unity(十七)Unity随机数及Unity委托
unity·游戏引擎
ellis19701 小时前
Unity性能优化之检测工具Profiler
unity·性能优化
Hello_Embed1 小时前
libmodbus 源码分析
笔记·stm32·单片机·嵌入式·ai编程
05候补工程师1 小时前
【408考研】数据结构核心笔记:单链表与栈操作精髓总结
数据结构·笔记·考研·链表·c#
kdxiaojie1 小时前
U-Boot分析【学习笔记】(7)
linux·笔记·学习
Huanzhi_Lin1 小时前
skynet笔记
笔记·lua·skynet·actor·actor模型
RPGMZ3 小时前
RPGMZ游戏引擎 一个窗口 文本居中显示
开发语言·javascript·游戏引擎·rpgmz
爱看大明王朝156610 小时前
磁件学习-磁性元器件的极限计算
笔记·学习
问心无愧051310 小时前
ctf show web入门 40
笔记