08-自适应相机控制
1. 概述
你好,我是悦创。
本次课,我们要实现是摄像机的移动跟随,不同设备上,同样的画面显示。
为了实现这个功能,我们会在我们的 Camera 上面挂一个代码。
用来实时检测我现在屏幕的宽度和高度。
获得实际的比例,因为我们现在常见的手机款式都不太一样。
有 16:9、有 18:9......
所以,我们根据他的比例,进行调整我们的 size。
size 数字越小离我们显示的内容就越近,越大就越远。
所以,我们可以先找到一个基数,我们以 iPhone 13 Pro Max 为基准的话,我看到比例是 8.733592 还 ok。所以,我们会根据它的长宽比例,乘以这个基数。接着再乘以一半,我们之前在代码手册发现本来就可以等于它高度的一半。
所以,我们先创建我们的代码。
2. 创建代码文件
在 Scripts 里面创建文件夹 Came,然后创建 CameraContrlo.cs。
把代码添加到相机里面:
接着就可以双击打开我们的代码编辑器了。
3. 代码编写
首先,相机想要实现跟随,我们需要实时获取到青蛙的位置。
所以,我们可以创建变量,拿到我们当前的位置。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
public Transform frog; // 这个创建的类型,取决于我们想要用到 Unity 身上的哪些组件。
// public GameObject frog; // 如果使用 GameObject,我们就需要 GameObject.Transform 然后获取坐标,这样显然太复杂而麻烦。
// 拿到 Transform 坐标之后呢?我们需要实时的跟随。
// 作为摄像机跟随,如果跟青蛙保持在同一个更新的频率上,很可能会造成画面的抖动。
// 因为,Update 是根据不同设备来进行更新的。
// 所以,我希望在 Frog 更新它的坐标的下一帧,然后才更新相机的坐标位置。
// 所以,我们使用周期函数 LateUpdate
private void LateUpdate() {
// 在这里更新相机的 position;
transform.position = new Vector3(transform.position.x, ) // x and z 是保持不变。y 轴要有一个位移差。
// 如果 y 轴完全等于我们青蛙的坐标会怎么样呢?「讲义有写」
}
}
如果 y 轴完全等于我们青蛙的坐标会怎么样呢?
下面会一直是空的,也会导致我们看不见前面的路。
所以,我们需要有一定的位移。这个位移也需要我们屏幕的比例来进行调整。所以,也需要有一个变量来控制画面比例的位移。
之后,我们会反复的测试,找到我们需要的值。
所以,我们创建变量。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
public Transform frog; // 这个创建的类型,取决于我们想要用到 Unity 身上的哪些组件。
// public GameObject frog; // 如果使用 GameObject,我们就需要 GameObject.Transform 然后获取坐标,这样显然太复杂而麻烦。
// 拿到 Transform 坐标之后呢?我们需要实时的跟随。
// 作为摄像机跟随,如果跟青蛙保持在同一个更新的频率上,很可能会造成画面的抖动。
// 因为,Update 是根据不同设备来进行更新的。
// 所以,我希望在 Frog 更新它的坐标的下一帧,然后才更新相机的坐标位置。
// 所以,我们使用周期函数 LateUpdate
public float offsetY;
private void LateUpdate()
{
// 在这里更新相机的 position;
transform.position = new Vector3(transform.position.x, transform.position.y + offsetY, transform.position.z); // x and z 是保持不变。y 轴要有一个位移差。
// 如果 y 轴完全等于我们青蛙的坐标会怎么样呢?「讲义有写」
}
}
我们的设备,还需要获取当前的长宽的比例。
因为不同的比例,我们的 Y 值也是要考虑不同的更改。
当然,上面的代码是错误❌的,因为我们除了 x、z 不变之外。y 是要青蛙的坐标。修改如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
public Transform frog; // 这个创建的类型,取决于我们想要用到 Unity 身上的哪些组件。
// public GameObject frog; // 如果使用 GameObject,我们就需要 GameObject.Transform 然后获取坐标,这样显然太复杂而麻烦。
// 拿到 Transform 坐标之后呢?我们需要实时的跟随。
// 作为摄像机跟随,如果跟青蛙保持在同一个更新的频率上,很可能会造成画面的抖动。
// 因为,Update 是根据不同设备来进行更新的。
// 所以,我希望在 Frog 更新它的坐标的下一帧,然后才更新相机的坐标位置。
// 所以,我们使用周期函数 LateUpdate
public float offsetY;
private void LateUpdate()
{
// 在这里更新相机的 position;
transform.position = new Vector3(transform.position.x, frog.transform.position.y + offsetY, transform.position.z); // x and z 是保持不变。y 轴要有一个位移差。
// 如果 y 轴完全等于我们青蛙的坐标会怎么样呢?「讲义有写」
}
}
其实实际情况下,我们还需要乘以画面比例的一半。我后面计算,现在可以先测试。
4. 在 Unity 中给变量赋值
先设置为 3,之后在调整。
保存你的项目,点击运行。
5. 测试运行
我们会发现背景一开始就往前移动了。
我们再换一个极端的手机机型。
背景也往前移动了。左右的宽度也不如我们之前的宽了,看到的也更少了。
所以,我希望它能看见更多,通过一个计算的方法,拿到比例,通过调高 size 的。同时我们调大这个 size 我们离下方也越来越远的过程。
同时,我希望这个比例也会影响我这个位移的差。所以,我们来进行进一步的调整。
6. 修复比例问题
切换回原本的 iPhone13 Pro max。
我们如何获得屏幕的比例呢?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
public Transform frog; // 这个创建的类型,取决于我们想要用到 Unity 身上的哪些组件。
// public GameObject frog; // 如果使用 GameObject,我们就需要 GameObject.Transform 然后获取坐标,这样显然太复杂而麻烦。
// 拿到 Transform 坐标之后呢?我们需要实时的跟随。
// 作为摄像机跟随,如果跟青蛙保持在同一个更新的频率上,很可能会造成画面的抖动。
// 因为,Update 是根据不同设备来进行更新的。
// 所以,我希望在 Frog 更新它的坐标的下一帧,然后才更新相机的坐标位置。
// 所以,我们使用周期函数 LateUpdate
public float offsetY;
// unity 中可以通过内置的 Screen,就可以获取得到它的宽和它的高
private float ratio; // 用来计算比例
private void Start() {
ratio = Screen.height / Screen.width;
Debug.Log(ratio);
}
private void LateUpdate()
{
// 在这里更新相机的 position;
transform.position = new Vector3(transform.position.x, frog.transform.position.y + offsetY, transform.position.z); // x and z 是保持不变。y 轴要有一个位移差。
// 如果 y 轴完全等于我们青蛙的坐标会怎么样呢?「讲义有写」
}
}
我们要暂时关闭 Unity 的历史清除功能。
接下来运行,并观察比例:
我们可以看见,每次输出的比例都是 2,但是这样的比例其实是不对的。
我们明知道它的宽和高不可能都是 2。——这是为什么呢?
因为,我们需要保证在计算的时候,要保证其中有一个是浮点数,现在都是 int。
所以,取整就会导致结果存在问题。
所以,解决方法,就是我们需要进行强制转换成 float:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
public Transform frog; // 这个创建的类型,取决于我们想要用到 Unity 身上的哪些组件。
// public GameObject frog; // 如果使用 GameObject,我们就需要 GameObject.Transform 然后获取坐标,这样显然太复杂而麻烦。
// 拿到 Transform 坐标之后呢?我们需要实时的跟随。
// 作为摄像机跟随,如果跟青蛙保持在同一个更新的频率上,很可能会造成画面的抖动。
// 因为,Update 是根据不同设备来进行更新的。
// 所以,我希望在 Frog 更新它的坐标的下一帧,然后才更新相机的坐标位置。
// 所以,我们使用周期函数 LateUpdate
public float offsetY;
// unity 中可以通过内置的 Screen,就可以获取得到它的宽和它的高
private float ratio; // 用来计算比例
private void Start()
{
ratio = (float)Screen.height / (float)Screen.width;
Debug.Log(ratio);
}
private void LateUpdate()
{
// 在这里更新相机的 position;
transform.position = new Vector3(transform.position.x, frog.transform.position.y + offsetY, transform.position.z); // x and z 是保持不变。y 轴要有一个位移差。
// 如果 y 轴完全等于我们青蛙的坐标会怎么样呢?「讲义有写」
}
}
接着,我们再进行测试一下:
所以,我们可以看出来比例是在进行不断的变化的。
那么我们如何做这件事情呢?
我们把比例给到我们坐标差值 offsetY:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
public Transform frog; // 这个创建的类型,取决于我们想要用到 Unity 身上的哪些组件。
public float offsetY;
// unity 中可以通过内置的 Screen,就可以获取得到它的宽和它的高
private float ratio; // 用来计算比例
private void Start()
{
ratio = (float)Screen.height / (float)Screen.width;
Debug.Log(ratio);
}
private void LateUpdate()
{
// 在这里更新相机的 position;
transform.position = new Vector3(transform.position.x, frog.transform.position.y + offsetY * ratio, transform.position.z); // x and z 是保持不变。y 轴要有一个位移差。
// 如果 y 轴完全等于我们青蛙的坐标会怎么样呢?「讲义有写」
}
}
这样保证我这个高度,都是同一样的比例。
那么我,我们摄像机的的位置要怎么处理呢?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControl : MonoBehaviour
{
public Transform frog; // 这个创建的类型,取决于我们想要用到 Unity 身上的哪些组件。
public float offsetY;
// unity 中可以通过内置的 Screen,就可以获取得到它的宽和它的高
private float ratio; // 用来计算比例
public float zoomBase; // 我们需要一个基础的比例
private void Start()
{
ratio = (float)Screen.height / (float)Screen.width;
// Debug.Log(ratio);
// 我们既然有这个比例,用基础的数值,乘以比例、乘以一半
Camera.main.orthographicSize = zoomBase * ratio * 0.5f; // 使用这个代码就可以获取 Unity 当中被标记为 Main Camera 的主相机
}
private void LateUpdate()
{
// 在这里更新相机的 position;
transform.position = new Vector3(transform.position.x, frog.transform.position.y + offsetY * ratio, transform.position.z); // x and z 是保持不变。y 轴要有一个位移差。
// 如果 y 轴完全等于我们青蛙的坐标会怎么样呢?「讲义有写」
}
}
这个是一个固定的标签,代表是一个主摄像机。
回到我们的 Unity 进行设置我们的 Zoom Base:
接下来,可以运行测试了。
记住这个画面
现在我们可以看见,画面是一样的,类似的。这样我们实现了,不同设备显示的画面比例。
不会因为设备宽而看到更宽,通这种方式适配各种设备显示。
当然,现在还是存在问题。
如果换到更长的设备,我试一试看。
虽然,上面实现了跟随。但是我们的背景有限,如何解决玩家长时间玩游戏呢?背景显然不有确切的数据。
有同学会说,把摄像机和背景放在一起,也就是背景挂在相机上。我们来试一试:
父级物体移动子物体也会移动。
这就出现了另外一种问题:我们的宽度不能完全适配。
尤其是 Sprite Renderer,很难动态的去调节他的高度和宽度。
在 Unity 中我们还有另外一套工具来实现。——覆盖整个屏幕的 UI 组件。
7. Canvas
一般来说,我们游戏的分数、名称等,并不需要跟随我们的屏幕,而是固定。「不管是主机游戏还是手机游戏」
所以,我们使用 Canvas 实时覆盖我们的画面。
- Render Mode:Overlay 覆盖在我屏幕的最上方,所以没有任何东西可以阻挡他,这个非常适合我表面的 UI。「得分、按钮」
- Camera 根据我们的摄像机,这个摄像机的范围,需要我设置,哪个摄像机是主要摄像机。「把我们的 Main Camera 拖进去」
我们这个时候可以看见 Plane Distance 是 100。
也就是说,它距离我的摄像机有一百米那么远。还记得么,越远的距离,是越靠后的。
这个越远的距离,越靠后。所以,我们也可以选择 Sorting Layer,目前是 Default 也就是最底下的那层。
所以,我们只要在 BG 那层:
其实就是删除 Bg,在 Canvas 上有一个可以覆盖整个屏幕的背景。
这样,Canvas 就会根据我的范围,去修改这个 UI 面板。
8. Panel
可以很直观看见,Panel 已经很直观的覆盖我们的屏幕了。
接着你可以换任意设备,你会发现 Panel 会覆盖我整个设备。——这就是我们想要的效果。
9. 添加背景
我们可以发现,背景现在是浅绿色。
这样就实现了 Canvas 跟随我摄像机的渲染,保持在很远的距离,在最底下的。从这个绿色图片,填充我的摄像机。
接下来,我们测试一下。
在测试中,你会发现,下面会出现一堆报错。
原因是什么呢?
原因就是 EventSystem 是我们创建 UI 的时候同时一起创建进来的。
这个 EventSystem 使用的是旧的 InputManage。
所以,我们需要修改成新的输入系统。
点一下就可以了:
这样就可以了:
保存项目,测试运行。
清除输出,这样就有结果了。
可以运行,移动。
可以发现,现在青蛙会跟随运行。也就是绿色背景会跟着一起移动了。
其实,这不就是背景无限运行了。
包括我们底边的位移差,大家可以不停的调整,找到自己喜欢的位移差。
最终,我觉得 Offset Y=3 不错,我就设为了 3。
10. 注意⚠️
我 size 设置的是:8.733592。但是在运行的时候:
所以,这里有个小技巧就是,copy 运行的数值。
其实就是 9.447787,我直接设置即可。「也就是设置、暂停游戏」
欢迎关注我公众号:AI悦创,有更多更好玩的等你发现!
公众号:AI悦创【二维码】
AI悦创·编程一对一
AI悦创·推出辅导班啦,包括「Python 语言辅导班、C++ 辅导班、java 辅导班、算法/数据结构辅导班、少儿编程、pygame 游戏开发、Linux、Web全栈」,全部都是一对一教学:一对一辅导 + 一对一答疑 + 布置作业 + 项目实践等。当然,还有线下线上摄影课程、Photoshop、Premiere 一对一教学、QQ、微信在线,随时响应!微信:Jiabcdefh
C++ 信息奥赛题解,长期更新!长期招收一对一中小学信息奥赛集训,莆田、厦门地区有机会线下上门,其他地区线上。微信:Jiabcdefh
方法一:QQ
方法二:微信:Jiabcdefh
- 0
- 0
- 0
- 0
- 0
- 0