Unity--异步加载场景

news/2024/12/23 5:17:55/

Unity–异步加载场景

异步加载场景其实和异步加载资源是一样的,只是加载的内容比较特殊而已. 也可以将场景视为特殊资源.

1.SceneManager.LoadScene

加载场景的方式,在Unity 中加载场景是通过SceneManager.LoadScene("场景名称"); 来实现加载场景,这和UE4中的OpenLevel也是一样的. 其中SceneManager是untiy中自带的场景管理器,可以用于加载场景,卸载场景等.需要引入using UnityEngine.SceneManagement才能使用

2.同步加载场景

和资源一样,场景默认是同步加载的,也就是直接使用SceneManager.LoadScene("场景名称")来实现同步加载. 如果一个场景中的资源比较多,比如:游戏模型,粒子特效等,那么就会导致加载场景时候卡顿,很久才能加载场景.

需要注意的是,它会立即切换到新场景,这可能导致短暂的冻结或卡顿,特别是在加载较大或资源密集的场景时…为了解决这个问题,我们一般使用异步加载. 减轻主线程的压力.

3.异步加载场景

和异步加载资源一样,场景的异步加载也是有两个过程: 加载中与加载完成.

异步加载的重要性:异步加载(LoadSceneAsync)允许场景在后台加载,这样主线程可以继续处理其他任务,如更新UI、处理玩家输入等。这对于提高用户体验至关重要,特别是在资源密集型游戏中。

仔细分析就是因为资源过大,内容过多导致加载中的时间过长,我们一般的设计方式就是进度条,加载完毕一段内容,进度条走了20%或者其他.直到加载完毕才走到100%,当然这个进度条有可能是假的.

在Unity中异步加载场景的写法如下:使用异步加载关键 + 加载完毕的回调函数

/// <summary>
/// 普通异步加载场景 + 调用回调函数
/// </summary>
/// <param name="scenenName">场景名称</param>
void LoadSceneAsychonized(string sceneName)
{// 加载场景AsyncOperation operation = SceneManager.LoadSceneAsync(sceneName);operation.completed += LoadSceneCompleted;
}

其中SceneManager.LoadSceneAsync(sceneName);sceneName就是我们要加载的场景名称. 返回值是一个异步加载操作的对象AsyncOperation .和上面将的一样,加载场景有两个状态:加载中与加载完毕.

AsyncOperation对象:其中AsyncOperation operation就是记录了场景是否加载完毕isDown,没有加载完毕就是在加载中. 当场景处于加载中,我们就能获取场景的加载进度progress, 优先级priority以及当场景准备好了就激活场景allowSceneActivation. 还有加载完毕的回调函数completed.
AsyncOperation对象:这是异步加载的核心。它提供了加载进度(progress)、是否完成(isDone)等重要信息。通过这些属性,可以创建进度条或执行其他加载相关的逻辑。

属性/方法含义
isDown是否加载完成
progress场景的加载进度0-1的值,Unity很多时候是0.9,这个值准确
priority优先级
allowSceneActivation收否在场景准备好了就激活场景
completed加载完毕的回到函数

以下是AsyncOperationC#中的代码

namespace UnityEngine
{//// 摘要://     Asynchronous operation coroutine.[NativeHeader("Runtime/Export/Scripting/AsyncOperation.bindings.h")][NativeHeader("Runtime/Misc/AsyncOperation.h")][RequiredByNativeCode]public class AsyncOperation : YieldInstruction{public AsyncOperation();~AsyncOperation();// 摘要:Has the operation finished? (Read Only)public bool isDone { get; }// 摘要: What's the operation's progress. (Read Only)public float progress { get; }// 摘要: Priority lets you tweak in which order async operation calls will be performed.public int priority { get; set; }// 摘要:Allow Scenes to be activated as soon as it is ready.public bool allowSceneActivation { get; set; }public event Action<AsyncOperation> completed;}
}

注意,场景加载完毕后我们需要用一个函数来做一些其他内容, 比如:设置场景初始化[这里需要说明的是加载场景不等于初始化场景],还可以设置游戏状态,UI的显示隐藏等.

 private void LoadSceneCompleted(AsyncOperation operation){// 场景加载完成后执行的代码Debug.Log("Scene loaded successfully");// ... ...// 在这里可以进行场景初始化,例如查找和初始化游戏对象,设置游戏状态等}

4.使用协程的方式异步加载场景

利用AsyncOperation operation的isDone数显来判断是否加载完毕, 如果没有加载完毕,就不可以做一些其他事情,并使用yield return来等待一段时间,然后继续判断是否加载完毕,代码如下:

/// <summary>/// 自定义协程加载场景/// </summary>/// <param name="operation"></param>/// <returns></returns>IEnumerator LoadWaitScene(AsyncOperation operation){// 获得加载进度while(! operation.isDone){Debug.Log("加载中...\t进度: " + operation.progress);if (operation.progress >= 0.9f){// 激活场景 Allow Scenes to be activated as soon as it is ready.operation.allowSceneActivation = true;}// 自己做个假的进度条yield return null;}}

我们也可以直接在协程里使用yield return operaiton来判断是否记加载完毕, 需要注意的是,一旦获得了加载操作的对象那么yeild return xxx后的代码就无法执行.因为场景加载好了,切换到了新的场景,旧的场景中的内容会被销毁,也包括我们挂载的脚本

IEnumerator LoadScene(string sceneName){DontDestroyOnLoad(this.gameObject);AsyncOperation operation = SceneManager.LoadSceneAsync(sceneName);Debug.Log("加载中...");yield return operation;// 后面的内容无法打印,因为场景被加载完毕,当前场景上游戏物体,脚本被移除Debug.Log("场景加载完毕后打印数据");// 要想场景加载完毕后也可以继续执行yield return 后的代码,需要使用 DontDestroyOnLoad 来保存数据// 注意: DontDestroyOnLoad 这个代码要放在异步加载场景之前的任意位置,可以是在协程前,可以是在开启异步加载场景协程前.Debug.Log("加载场景时不销毁对象");// 场景加载结束,但不急着显示场景// 场景加载结束, 进度条更新一段// 接着加载场景中的其他信息// 加载怪物-怪物加载完毕进度条更新一段// 动态加载 场景模型// 这时候就认为加载完毕,进度条设置100%, 隐藏进度条}

5.DontDestroyOnLoad

如何保持旧场景指定游戏对象/脚本/组件不被销毁? 这时候需要使用DontDestroyOnLoad这个方法来让我们指定的兑现不销毁.下面的代码表示加载场景后销毁气其他资源 ,不销毁当前脚本挂载的游戏物体,自然,当前脚本就不会被销毁了. DontDestroyOnLoad()这是一个重要的方法,用于在场景切换时保留特定的游戏对象。这在某些情况下非常有用,比如保留音效管理器或全局配置对象

 DontDestroyOnLoad(this.gameObject);

6.自己写一个场景管理器

为了避免每一次加载场景的时候都要自己手动写鞋厂或者回调函数,我们可以将这样的方案构成一个类, 值需要传入一个场景名称和一个加载完毕的函数名称就行. 该类最好可以在任意地方使用,因此,我们可以将场景管理类写成一个单例. 这和Unity自带的ScenManager是一个意思,只是自己有了自己自定义的部分. 代码如下:

public class MySceneManager
{private static MySceneManager instance = new MySceneManager();private MySceneManager() { }public static MySceneManager Instance => instance;/// <summary>/// 外部调用异步加载场景的方法/// </summary>/// <param name="sceneName"> 场景名 </param>public void LoadScene(string sceneName, UnityAction action){AsyncOperation ao = SceneManager.LoadSceneAsync(sceneName);ao.completed += (a) =>{action(); // 调用外部的函数};}
}

测试脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class TestMySceneManager : MonoBehaviour
{    void Update(){if(Input.GetKeyDown(KeyCode.A)){MySceneManager.Instance.LoadScene("测试场景", loadCompleteAction);}}private void loadCompleteAction(){Debug.Log("场景加载完毕");}
}

7.测试和优化:

在实现异步加载时,测试不同的场景大小和资源负载非常重要。这有助于发现潜在的性能瓶颈并优化加载过程。

8.资源打包和加载策略:

除了异步加载,合理的资源打包和加载策略也对性能有显著影响。考虑使用AssetBundlesAddressables来优化资源的加载和管理。

9.用户体验:

在加载过程中,提供清晰的反馈(如进度条、加载动画)对于提升用户体验至关重要。这可以让玩家知道游戏正在加载,而不是卡顿或无响应。

最后,确保在实现异步加载时,对Unity的版本和平台特性有一定的了解,因为它们可能会影响异步加载的行为和性能。


http://www.ppmy.cn/news/1474075.html

相关文章

关于ORACLE单例数据库中的logfile的切换、删除以及添加

一、有关logfile的状态解释 UNUSED&#xff1a; 尚未记录change的空白group&#xff08;一般会出现在loggroup刚刚被添加&#xff0c;或者刚刚使用了reset logs打开数据库&#xff0c;或者使用clear logfile后&#xff09; CURRENT: 当前正在被LGWR使用的gro…

Qt扫盲-QRect矩形描述类

QRect矩形描述总结 一、概述二、常用函数1. 移动类2. 属性函数3. 判断4. 比较计算 三、渲染三、坐标 一、概述 QRect类使用整数精度在平面中定义一个矩形。在绘图的时候经常使用&#xff0c;作为一个二维的参数描述类。 一个矩形主要有两个重要属性&#xff0c;一个是坐标&am…

HBuilder X 小白日记03-用css制作简单的交互动画

:hover选择器&#xff0c;用于选择鼠标指针浮动在上面的元素。 :hover选择器可用于所有元素&#xff0c;不只是链接 :link选择器 设置指向未被访问页面的链接的样式 :visited选择器 用于设置指向已被访问的页面的链接 :active选择器 用于活动链接

爬虫逆向之常见的JS Hook示例

爬虫逆向之常见的JS Hook示例 在JavaScript中&#xff0c;hook通常指的是通过替换或修改函数、属性或对象来拦截或修改程序行为的技术。 以下是一些常见的hook示例&#xff1a; 函数挂钩&#xff08;Function Hooking&#xff09;: // 原始函数 function originalFunction() …

【面试题】串联探针和旁挂探针有什么区别?

在网络安全领域中&#xff0c;串联探针和旁挂探针&#xff08;通常也被称为旁路探针&#xff09;是两种不同部署方式的监控设备&#xff0c;它们各自具有独特的特性和应用场景。以下是它们之间的主要区别&#xff1a; 部署方式 串联探针&#xff1a;串联探针一般通过网关或者…

Yolov10训练,转化onnx,推理

yolov10对于大目标的效果好&#xff0c;小目标不好 一、如果你训练过yolov5&#xff0c;yolov8&#xff0c;的话那么你可以直接用之前的环境就行 目录 一、如果你训练过yolov5&#xff0c;yolov8&#xff0c;的话那么你可以直接用之前的环境就行 二、配置好后就可以配置文件…

Qt 网络编程 网络信息获取操作

学习目标&#xff1a;网络信息获取操作 前置环境 运行环境:qt creator 4.12 学习内容 一、Qt 网络编程基础 Qt 直接提供了网络编程模块,包括基于 TCP/IP 的客户端和服务器相关类,如 QTcpSocket/QTcpServer 和 QUdpSocket,以及实现 HTTP、FTP 等协议的高级类,如 QNetworkRe…

213.贪心算法:跳跃游戏||(力扣)

class Solution { public:int jump(vector<int>& nums) {if (nums.size() 1) return 0; // 如果数组长度为1&#xff0c;已经在终点&#xff0c;不需要跳跃int cur 0; // 当前跳跃能到达的最远位置int flag 0; // 记录跳跃次数int next 0; // 下一次跳跃能到…