问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Unity引擎异步实例化功能详解

创作时间:
作者:
@小白创作中心

Unity引擎异步实例化功能详解

引用
1
来源
1.
https://docs.unity.cn/cn/tuanjiemanual/Manual/InstantiateAsync.html

在游戏开发中,同时实例化大量对象容易导致卡顿,影响用户体验。Unity引擎提供了AssetBundle.InstantiateAsync和Object.InstantiateAsync两种异步实例化方案,可以有效提升性能并保持游戏流畅性。

AssetBundle.InstantiateAsync

功能介绍

为了避免同时实例化大量AssetBundle中的游戏对象而导致的卡顿,用户可以利用InstantiateAsync API进行异步实例化,以此来提升性能并保持游戏流畅性。当需要在场景中创建游戏对象时,只需在AssetBundle加载完成之后,异步调用InstantiateAsync API。使用InstantiateAsync API不会影响原始AssetBundle的工作流程。

实现步骤

public class Example : MonoBehaviour
{
    AssetBundle myLoadedAssetBundle;
    void Start()
    {
        // Load Assetbundle
        myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle"));
    }
    void Update()
    {
        // Press the space key to start coroutine
        if (Input.GetKeyDown(KeyCode.Space))
        {
            if (myLoadedAssetBundle == null)
            {
                Debug.Log("Failed to load AssetBundle!");
                return;
            }
            // Use a coroutine to Instantiate in the background
            StartCoroutine(InstantiateAsyc());
        }
    }
    IEnumerator InstantiateAsyc()
    {
        yield return myLoadedAssetBundle.InstantiateAsync(myLoadedAssetBundle.GetAllAssetNames()[0]);
    }
}

Object.InstantiateAsync

执行过程

用户代码中调用Instantiate时,引擎内分为两步执行:

  1. 通过transfer序列化构建出对象;
  2. 通过IntegrateMainThread初始化新创建的对象,进入引擎事件生命周期。

使用同步Instantiate时,这两步会在同一帧内完成。小游戏平台上的InstantiateAsync方法通过分帧处理来优化实例化过程,将对象构建和初始化操作拆分到两帧中执行:

  1. 执行第一步transfer序列化构建对象时,会使用JobSystem来加速,但依旧会在一帧内完成;
  2. 在第二步IntegrateMainThread增加了一个队列。如果单帧时间内有多次的InstantiateAsync调用,则可根据IntegrationTime的时间长度,分多帧完成。

小游戏分帧Integration

由于小游戏是单线程,使用JobSystem加速transfer序列化构建对象无法达到预期的效果。另外对于单次实例包含大量对象,例如包含Collider、Renderer等的Prefab,在完成IntegrateMainThread进入引擎事件生命周期后,首次更新会有较长的创建物理几何体、编译shader等耗时。

因此,从实际的测试来看,执行第二步IntegrateMainThread所在的那一帧总时间远高于执行第一步构建对象所在的帧。因此在小游戏平台上,我们对第二步IntegrateMainThread进一步分帧处理,允许单次InstantiateAsync调用能够拆分成更多帧执行。当前版本为保证Renderer和粒子等继承自Behaviour的对象运行时序正确,允许最多分成4帧执行。纹理、shader等资源以及用户的MonoBehaviour不受该限制影响。

实例化方式
实例化总耗时
单帧最高时长
7帧总耗时
平均每帧耗时
Instantiate
73.2ms
73.2ms
160ms
22.8ms
InstantiateAsync
84ms
35.6ms
135ms
19.2ms

从测试结果看,在实例化相同的对象时,单帧最高时长为73.10ms。替换为InstantiateAsync后,降低为35.6ms。另外,异步接口因为将工作负载拆分到了4帧完成,7帧总耗时降低为135ms,同步接口耗时约160ms。

注意事项

  1. InstantiateAsync的分帧效果受IntegrationTime时长设置影响较大,默认为每帧2ms,可通过AsyncInstantiateOperation.SetIntegrationTimeMS接口设置时长。更小的值能够减少卡顿,但会延长实例化的总耗时。

  2. 使用InstantiateAsync接口时需要注意,在完成对象的实例化前,不能提前卸载对象所在的AB和其依赖AB。否则可能出现材质或Prefab引用丢失的情况,导致游戏逻辑或渲染出错。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号