需求:
在游戏开发中,我们经常会遇到以下情况:
1,烘焙场景太大,每次只需要烘焙部分;
2,资源热更,需要分步加载;
3,场景跳转太慢,需手动处理资源;
4,网页游戏不方便场景跳转。
诸如此类问题,就需要将资源分配到不同的小场景分别进行烘焙,最后统一到综合场景中进行合并加载。
解决思路:
这里主要参考了这两篇博客:Unity在一个场景中使用其他场景烘焙的物体,Unity Lightmap使用总结。
其中第一篇博客提供了解决思路,但年代久远;第二篇不太完整,所以取舍之后做出如下解决方案。
注意:在以下解决方案测试中,渲染环境基本保持一致;如果不一致需自己进行扩充。
解决方案:
第一步:烘焙场景
将父节点及之下需烘焙的子节点勾选静态模式,自行设置参数进行烘焙:
烘焙会出现场景同名文件夹,如下图所示:
烘焙场景可以拷贝,这样能保持烘焙信息一致;另外拷贝一份删除所有信息,作为主场景。
第二步:代码处理
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;
}
}
将该脚本拖到主场景中,并将各个分场景烘焙的贴图赋值到光照贴图数组中,运行结果如下:
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文件夹中,另一个赋值给各个分场景的预制体父节点,以记录烘焙信息。使用结果如下:
这里的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()
{
}
}
测试结果为:
测试工程连接为:
四,后记
这种方法在流程化的情况下,操作起来比较简单,但还是有如下不足之处:
1,因为静态的缘故,会占用双份内存;
2,加载操作比较麻烦,需要分步处理;