需求:

在游戏开发中,我们经常会遇到以下情况:

1,烘焙场景太大,每次只需要烘焙部分;

2,资源热更,需要分步加载;

3,场景跳转太慢,需手动处理资源;

4,网页游戏不方便场景跳转。

诸如此类问题,就需要将资源分配到不同的小场景分别进行烘焙,最后统一到综合场景中进行合并加载。

 

解决思路:

这里主要参考了这两篇博客:Unity在一个场景中使用其他场景烘焙的物体Unity Lightmap使用总结

其中第一篇博客提供了解决思路,但年代久远;第二篇不太完整,所以取舍之后做出如下解决方案。

注意:在以下解决方案测试中,渲染环境基本保持一致;如果不一致需自己进行扩充。

 

解决方案:

第一步:烘焙场景

将父节点及之下需烘焙的子节点勾选静态模式,自行设置参数进行烘焙:

unity 官方 超大场景 加载 unity场景加载很慢_合并光照贴图

unity 官方 超大场景 加载 unity场景加载很慢_合并光照贴图_02

烘焙会出现场景同名文件夹,如下图所示:

unity 官方 超大场景 加载 unity场景加载很慢_烘焙_03

烘焙场景可以拷贝,这样能保持烘焙信息一致;另外拷贝一份删除所有信息,作为主场景。

 

第二步:代码处理

1,主场景渲染管理器,这个主要是控制场景的光照贴图信息,所以要放到Awake中启动,并在需要的时候刷新:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LightMapControl : MonoBehaviour
{
  
    [SerializeField]
    public List<Texture2D> _renderDatainfo = new List<Texture2D>();

    // Start is called before the first frame update
    void Awake()
    {
        SetLightMapData();
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            SetLightMapData();
        }
    }

    /// <summary>
    /// 对场景进行烘培灯光性息的赋值\n
    /// _renderDatainfo: LIST类型的贴图组
    /// </summary>
    public void SetLightMapData()
    {
       
        // //缔特
        // //整理灯光贴图
        // //创建临时贴图list,并复制导入数据进入
        LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
        List<Texture2D> Templightmaps = new List<Texture2D>(this._renderDatainfo.ToArray());
        // this._renderDatainfo.ForEach(i => Templightmaps.Add(i));
        // Templightmaps = this._renderDatainfo; //List<Texture2D> 
        //遍历当前场景数据,如果临时list里有就删除list里的数据,并按当前id插入进去,得到最终贴图的顺序
        for (int i = 0; i < LightmapSettings.lightmaps.Length; i++)
        {
            Texture2D nowTex = LightmapSettings.lightmaps[i].lightmapColor;
            int ia = Templightmaps.IndexOf(nowTex);
            if (ia != -1)
            {
                Templightmaps.RemoveAt(ia);
            }
            Templightmaps.Insert(i, LightmapSettings.lightmaps[i].lightmapColor);
        }
        //把贴图赋值给当前场景
        List<LightmapData> aa = new List<LightmapData>();
        Templightmaps.ForEach(i => aa.Add(this.setNewLightmapdata(i)));
        LightmapSettings.lightmaps = aa.ToArray();
    }

    private LightmapData setNewLightmapdata(Texture2D a)
    {
        LightmapData tt = new LightmapData();
        tt.lightmapColor = a;
        return tt;
    }
}

将该脚本拖到主场景中,并将各个分场景烘焙的贴图赋值到光照贴图数组中,运行结果如下:

unity 官方 超大场景 加载 unity场景加载很慢_游戏_04

 

2,记录保存和加载使用烘焙信息,这里需要将父节点设置为预制体:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(LightmapRoot))]
public class LightmapRootEditor : Editor
{
    private LightmapRoot Root
    {
        get { return target as LightmapRoot; }
    }

    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();
        GUILayout.Space(10);
        if (GUILayout.Button("SaveLightmapData"))
        {
            Root.GetAllLightmapData();
            SavePrefab();
        }

        GUILayout.Space(10);
        if (GUILayout.Button("SetLightmapData"))
        {
            Root.SetAllLightmapData();
        }
    }

    private void SavePrefab()
    {
        var prefab = PrefabUtility.GetCorrespondingObjectFromSource(Root);
        Debug.Log("记录预制体信息");
        if (prefab != null)
        {
            PrefabUtility.ReplacePrefab(Root.gameObject, prefab);
            Debug.Log(prefab);
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LightMapControl : MonoBehaviour
{

    [SerializeField]
    public List<Texture2D> _renderDatainfo = new List<Texture2D>();

    // Start is called before the first frame update
    void Awake()
    {
        SetLightMapData();
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            SetLightMapData();
        }
    }

    /// <summary>
    /// 对场景进行烘培灯光性息的赋值\n
    /// _renderDatainfo: LIST类型的贴图组
    /// </summary>
    public void SetLightMapData()
    {

        // //缔特
        // //整理灯光贴图
        // //创建临时贴图list,并复制导入数据进入
        LightmapSettings.lightmapsMode = LightmapsMode.NonDirectional;
        List<Texture2D> Templightmaps = new List<Texture2D>(this._renderDatainfo.ToArray());
        // this._renderDatainfo.ForEach(i => Templightmaps.Add(i));
        // Templightmaps = this._renderDatainfo; //List<Texture2D> 
        //遍历当前场景数据,如果临时list里有就删除list里的数据,并按当前id插入进去,得到最终贴图的顺序
        for (int i = 0; i < LightmapSettings.lightmaps.Length; i++)
        {
            Texture2D nowTex = LightmapSettings.lightmaps[i].lightmapColor;
            int ia = Templightmaps.IndexOf(nowTex);
            if (ia != -1)
            {
                Templightmaps.RemoveAt(ia);
            }
            Templightmaps.Insert(i, LightmapSettings.lightmaps[i].lightmapColor);
        }
        //把贴图赋值给当前场景
        List<LightmapData> aa = new List<LightmapData>();
        Templightmaps.ForEach(i => aa.Add(this.setNewLightmapdata(i)));
        LightmapSettings.lightmaps = aa.ToArray();
    }

    private LightmapData setNewLightmapdata(Texture2D a)
    {
        LightmapData tt = new LightmapData();
        tt.lightmapColor = a;
        return tt;
    }
}

 

使用方法是将一个放到Editor文件夹中,另一个赋值给各个分场景的预制体父节点,以记录烘焙信息。使用结果如下:

unity 官方 超大场景 加载 unity场景加载很慢_Unity3D_05

这里的basic是记录光照贴图初始位置的,下标从0开始。

 

3,在主场景中测试

测试代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        for (var _i = 1; _i <= 3; _i++)
        {
            GameObject _obj = Instantiate<GameObject>(Resources.Load<GameObject>("" + _i));
        }   
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

测试结果为:

unity 官方 超大场景 加载 unity场景加载很慢_Unity3D_06

 

 

测试工程连接为:

 

四,后记

 

这种方法在流程化的情况下,操作起来比较简单,但还是有如下不足之处:

1,因为静态的缘故,会占用双份内存;

2,加载操作比较麻烦,需要分步处理;