跳至主要內容

08-自适应相机控制

AI悦创原创Unity休闲手机游戏开发UnityUnity休闲手机游戏开发Unity大约 15 分钟...约 4645 字

1. 概述

你好,我是悦创。

本次课,我们要实现是摄像机的移动跟随,不同设备上,同样的画面显示。

为了实现这个功能,我们会在我们的 Camera 上面挂一个代码。

用来实时检测我现在屏幕的宽度和高度。

获得实际的比例,因为我们现在常见的手机款式都不太一样。

有 16:9、有 18:9......

所以,我们根据他的比例,进行调整我们的 size。

size 数字越小离我们显示的内容就越近,越大就越远。

所以,我们可以先找到一个基数,我们以 iPhone 13 Pro Max 为基准的话,我看到比例是 8.733592 还 ok。所以,我们会根据它的长宽比例,乘以这个基数。接着再乘以一半,我们之前在代码手册发现本来就可以等于它高度的一半。

所以,我们先创建我们的代码。

2. 创建代码文件

在 Scripts 里面创建文件夹 Came,然后创建 CameraContrlo.cs。

把代码添加到相机里面:

1

接着就可以双击打开我们的代码编辑器了。

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 轴完全等于我们青蛙的坐标会怎么样呢?

1

下面会一直是空的,也会导致我们看不见前面的路。

所以,我们需要有一定的位移。这个位移也需要我们屏幕的比例来进行调整。所以,也需要有一个变量来控制画面比例的位移。

之后,我们会反复的测试,找到我们需要的值。

所以,我们创建变量。

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. 测试运行

iPhone Pro Max

我们会发现背景一开始就往前移动了。

我们再换一个极端的手机机型。

所以,我希望它能看见更多,通过一个计算的方法,拿到比例,通过调高 size 的。同时我们调大这个 size 我们离下方也越来越远的过程。

1

同时,我希望这个比例也会影响我这个位移的差。所以,我们来进行进一步的调整。

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 的历史清除功能。

1

接下来运行,并观察比例:

iPhone Pro Max

我们可以看见,每次输出的比例都是 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 轴完全等于我们青蛙的坐标会怎么样呢?「讲义有写」
    }

}


















 










接着,我们再进行测试一下:

Samsung Galaxy Z Fold2 5G

所以,我们可以看出来比例是在进行不断的变化的。

那么我们如何做这件事情呢?

我们把比例给到我们坐标差值 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 轴完全等于我们青蛙的坐标会怎么样呢?「讲义有写」
    }

}
















 









相机被标记为 MainCamera
相机被标记为 MainCamera

这个是一个固定的标签,代表是一个主摄像机。


回到我们的 Unity 进行设置我们的 Zoom Base:

接下来,可以运行测试了。

iPhone13 Pro Max

记住这个画面

现在我们可以看见,画面是一样的,类似的。这样我们实现了,不同设备显示的画面比例。

不会因为设备宽而看到更宽,通这种方式适配各种设备显示。

当然,现在还是存在问题。

如果换到更长的设备,我试一试看。

虽然,上面实现了跟随。但是我们的背景有限,如何解决玩家长时间玩游戏呢?背景显然不有确切的数据。

有同学会说,把摄像机和背景放在一起,也就是背景挂在相机上。我们来试一试:

父级物体移动子物体也会移动。

这就出现了另外一种问题:我们的宽度不能完全适配。

尤其是 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

1

可以很直观看见,Panel 已经很直观的覆盖我们的屏幕了。

接着你可以换任意设备,你会发现 Panel 会覆盖我整个设备。——这就是我们想要的效果。

9. 添加背景

1

这样就实现了 Canvas 跟随我摄像机的渲染,保持在很远的距离,在最底下的。从这个绿色图片,填充我的摄像机。

接下来,我们测试一下。

在测试中,你会发现,下面会出现一堆报错。

原因是什么呢?

原因就是 EventSystem 是我们创建 UI 的时候同时一起创建进来的。

这个 EventSystem 使用的是旧的 InputManage。

所以,我们需要修改成新的输入系统。

点一下就可以了:

这样就可以了:

保存项目,测试运行。

清除输出,这样就有结果了。

可以运行,移动。

可以发现,现在青蛙会跟随运行。也就是绿色背景会跟着一起移动了。

其实,这不就是背景无限运行了。

包括我们底边的位移差,大家可以不停的调整,找到自己喜欢的位移差。

1

最终,我觉得 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

方法一:QQopen in new window

方法二:微信:Jiabcdefh

上次编辑于:
贡献者: AndersonHJB
你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度