Unity中加载一些东西时候.比如场景或者大量预制体的情况.
有几种常用的平滑加载的方法.
Unity6里好像有一种基于Streaming形式的地图分块流形式加载技术,暂时没看到技术细节.
分帧平滑加载资源---根据性能,把资源逐个加载进来.分散性能消耗到每一帧.一般用在背包或者是,图标,物品比较多的情况,非整个场景的情况.
异步加载.---后台进程加载资源.不会阻塞主线程.---注意,有人提到图片的格式,如果选择RGB24模式可能会导致异步加载也卡,RGBA 32则不会卡.---可能和GPU支持有关如果异步加载AssetBundle,不压缩的情况下会最快,注意是否和LZ4的压缩格式有关.
有时候加载时候性能消耗还好,主要是实例化时候Object显示的时候,会导致卡顿,所以加载时候可以直接加载(INSTANTIATE)所有,但是显示的时候,SetActive的时候,进行平衡处理.
Unity中的异步加载(Asynchronous Loading)和分帧平滑加载(Load Smoothing Across Frames)虽然都是为了减轻游戏运行时的性能压力,但它们的原理和用途有所不同。
异步加载(Asynchronous Loading):
原理:异步加载允许游戏在后台加载资源,而不会阻塞主线程。这意味着游戏可以在加载新场景或资源的同时继续运行,从而不会造成明显的停顿或冻结。
实现:Unity 提供了AsyncOperation类和SceneManager.LoadSceneAsync方法,用于异步加载场景。这些操作通常涉及以下步骤:
开始加载操作,通常是通过调用LoadSceneAsync。
在后台线程中加载资源。
通过AsyncOperation的progress属性来监控加载进度。
加载完成后,通常会有一个回调函数来处理后续逻辑。
分帧平滑加载(Load Smoothing Across Frames):
原理:分帧加载是一种优化手段,它通过将加载工作分散到多个帧中来减少单帧的性能开销。这种方法并不一定是异步的,但它确保了每帧的处理时间保持在一个合理的范围内,从而避免帧率下降。
实现:分帧加载通常是通过以下步骤手动实现的:
评估每帧可以处理的加载量。
在每帧的开始或结束时,加载一定量的资源。
通过检查加载进度或剩余工作来决定是否继续在下一帧加载。
对比:
目的:异步加载主要是为了不阻塞主线程,而分帧加载主要是为了平衡每帧的工作量。
线程:异步加载可能涉及后台线程,而分帧加载通常在主线程上进行,但是将工作分散到多个帧中。
使用场景:异步加载适用于大型资源的加载,如整个场景或大量的数据,而分帧加载适用于可以在多个帧中逐步加载的资源,以避免帧率下降。
总的来说,虽然两者都是为了优化加载性能,但它们的实现方式和应用场景是不同的。开发者会根据具体需求选择最合适的加载策略。
下面是一个异步加载场景的例子.主要使用UnityEngine.SceneManagement;中的异步加载的形式.
SceneManager.LoadSceneAsync((int)SceneIndex.Main, LoadSceneMode.Additive); 下面是一个分帧加载的范例
public class SmoothLoader : MonoBehaviour
{
public GameObject[] prefabs; // 预制体列表
public int objectsPerFrame = 1; // 每帧加载的物体数量
//读取频率
public int frameInterval = 15;
private Queue<GameObject> prefabQueue = new Queue<GameObject>();
[Button("Load")]
void LoadTest()
{
// 将所有预制体加入队列
foreach (GameObject prefab in prefabs)
{
prefabQueue.Enqueue(prefab);
}
// 开始分帧加载物体
StartCoroutine(LoadObjects());
}
IEnumerator LoadObjects()
{
while (prefabQueue.Count > 0)
{
//显示加载的进度
Debug.Log("加载进度" + prefabQueue.Count);
// 加载指定数量的物体
for (int i = 0; i < objectsPerFrame; i++)
{
if (prefabQueue.Count > 0)
{
GameObject spawned;
GameObject prefab = prefabQueue.Dequeue();
spawned = Instantiate(prefab, transform);
spawned.SetActive(true);
spawned.SetActive(false);
//实例化之后可以,setActive,然后再SetInActive.
}
else
{
//如果队列已经为空,结束协程
yield break;
}
}
for (int i = 0; i < frameInterval; i++)
{
// 等待下一帧
yield return null;
}
}
}
}